My Tkinter GUI wont open - python

list_ = [0,0]
menu = ""
subpage = ""
def main_menu(root):
print list_
menu = Frame(root)
button0 = Button(menu, text="Go To Subpage",
command=lambda: switch_page("sub"))
button0.grid(row = 0, column = 0)
button1 = Button(menu, text="Save Values",
command=lambda: save_values())
button1.grid(row = 0, column = 1)
global entry0
global entry1
entry0 = Entry(menu)
entry1 = Entry(menu)
entry0.pack()
entry1.pack()
return menu
def sub_menu(root):
subpage = Frame(root)
label0 = Label(text = list_[0])
label1 = Label(text = list_[1])
label0.grid(row = 0, column = 0)
label1.grid(row = 1, column = 0)
button2 = Button(subpage, text="Return To Main Page",
command = lambda: switch_page("main"))
button2.grid(row = 0, column = 0)
return subpage
def save_values():
list_ = []
e0 = entry0.get()
list_.append(e0)
e1 = entry1.get()
list_.append(e1)
print list_
def switch_page(page_name):
slaves = root.pack_slaves()
if slaves:
slaves[0].pack_forget()
pages[page_name].pack(fill="both", expand=True)
root = Tk()
pages = {
"main": main_menu(root),
"sub": sub_menu(root),
}
switch_page("main")
root.mainloop()
So my problem is that the GUI won't open I have tried putting it into another new python file and have tried to re-write it but slightly differently, which now won't open as soon as I use a .grid() function, once this .grid() function is removed it will show the GUI. Anyway for this code I am not sure in the slightest what is preventing it from opening because the research I have done has lead to me finding out I need to use root.mainloop() ,which I was already using and the other solution was to define the frame length and width which I thought was unnecessary because I had previously had the GUI working and not only that I have a frame created with widgets inside.
Any help is appreciated.
Also on side note in the switch page function I use the .pack_forget() function and was looking to see if there was a function which closed the page fully or would allow me to change a label while the page is open like a sort of refresh function(update idle task didn't work for what I needed it for)

You cannot use both grid and pack with windows that share the same parent. You are doing that here:
...
button0.grid(row = 0, column = 0)
...
entry0.pack()
entry1.pack()
You can mix and match grid and pack all you want within an application, but you can't use them both like you do because those widgets all have menu as a parent.
Also, you can change the attributes of any widget with the config method (eg: some_widget.configure(background="red")). You need to have a reference (variable) to that widget.

Related

tkinter - Changing variables assigned to labels causes duplicate window

I'm using python to interact with some excel spreadsheets. I have all that working and now I'm working on a UI using tkinter. I have 3 buttons one to pull the location of a data file, output file save location and I have a start button.
I'm trying to use a tkinter.Label to display the value of the first two buttons, example "c:/user/data_file". However, when ever I get the variable from the user and try to update the GUI with it, a copy of the window is created with the updated information. I need it to update directly to the current window seamlessly. I've been working to try to resolve this, but I just can't figure it out. Below is the code for my tkinter stuff.
def main():
def InputFilePrompt():
global InputFileLocation
InputFileLocation = askopenfilename()
update()
def OutputFilePrompt():
global OutputFileLocation
OutputFileLocation = filedialog.asksaveasfilename()
update()
def update():
root = Tk()
root.title("test")
root.resizable(width=TRUE,height=TRUE)
InputFile = Button(root, text = "input data", command = InputFilePrompt)
InputFile.grid(row = 0,column = 0)
InputFileValue = Label(root, text = InputFileLocation, bg = 'white')
InputFileValue.grid(row = 1,column = 0)
OutputFile = Button(root, text = "Compiled Data save loacation", command = OutputFilePrompt)
OutputFile.grid(row = 4,column = 0)
OutputFileValue = Label(root, text = "location: N/A", bg = 'white')
OutputFileValue.grid(row = 5,column = 0)
startButton = Button(root, text = "start", bg = 'light green', command = Excel)
startButton.grid(row = 7)
BlankUI = [0 for x in range(2)]
for blankspace in range(2):
BlankUI[blankspace] = Label(root, text = "")
BlankUI[0].grid(row = 2)
BlankUI[1].grid(row = 6)
root.mainloop()
update()
Error:
Here's a version that doesn't create the duplicate window. I've incorporated most of the suggestions I made in comments—except for the one about defining functions inside of other functions. The following still does this because doing so made it very easy to avoid using global variables (which are generally considered a poor programming practice).
Notice that there's no update() function. The values of the two tkinter.Labels are now being stored in two tkinter.StringVars objects instead of in regular Python strings. A StringVar is one of the tkinter so-called "Variable" classes. Their primary feature is that they will cause all widgets referencing them to automatically update themselves whenever their contents get changed. To use them in a Label, they're specified by using the textvariable= option (instead of the text= option) when the constructor is called.
Here's some documentation I found about them with more details on how they work.
import tkinter as tk
from tkinter.filedialog import askopenfilename, asksaveasfilename
def excel():
""" Undefined. """
pass
def main():
def get_input_file_location():
input_file_location.set(askopenfilename())
def get_output_file_location():
output_file_location.set(asksaveasfilename(confirmoverwrite=False))
root = tk.Tk()
root.title('test')
root.resizable(width=True, height=True)
input_file_location = tk.StringVar()
input_file_location.set('<undefined>')
output_file_location = tk.StringVar()
output_file_location.set('<undefined>')
input_file = tk.Button(root, text="Input data",
command=get_input_file_location)
input_file.grid(row=0, column=0)
input_file_value = tk.Label(root, textvariable=input_file_location,
bg='white')
input_file_value.grid(row=1, column=0)
output_file = tk.Button(root, text='Compiled data save loacation',
command=get_output_file_location)
output_file.grid(row=4, column=0)
output_file_value = tk.Label(root, textvariable=output_file_location,
bg='white')
output_file_value.grid(row=5, column=0)
startButton = tk.Button(root, text='start', bg='light green',
command=excel)
startButton.grid(row=7)
blank_ui = [tk.Label(root, text='') for _ in range(2)]
blank_ui[0].grid(row=2)
blank_ui[1].grid(row=6)
root.mainloop()
if __name__ == '__main__':
main()

I want to print the entry text from textbox to console. Below is my code

I am trying to make a GUI using python tkinter.
I want to print the entry text from textbox to console after the analyzebutton is pressed.
here is my code
root = Tk()
root.title('Title')
MiddleFrame = Frame(root)
BottomFrame = Frame(root)
TopFrame.pack(side = TOP)
MiddleFrame.pack()
BottomFrame.pack(side = BOTTOM)
TextArea = Text()
ScrollBar = Scrollbar(root)
ScrollBar.config(command = TextArea.yview)
TextArea.config(height = 25, width = 70,
background = "white", yscrollcommand = ScrollBar.set)
TextArea.grid(padx = 18, pady = 18)
ScrollBar.pack(side = RIGHT, fill = Y)
padx = 10
pady = 10
TextArea.pack(padx = padx, pady = pady)
AnalyzeButton = Button(BottomFrame, text = "Analyze", fg = "white", bg = "blue", command = callback)
AnalyzeButton.pack(fill = X, padx = padx, pady = pady)
def callback():
text_input = Text.get()
print(text_input)
root.mainloop()
thanks in advance
Use get method of Text. Use the widget not the class-Text. Here is what you need to do:-
text_input = TextArea.get("1.0","end-1c")
You have several problems in your code. I will break it down so you understand what is going on here.
Fist I noticed you have tried to pack TopFrame but you have not actually defined TopFrame yet. So I added TopFrame = Frame(root) to your code.
Next we have a common mistake people encounter when trying to use grid() and pack() on the same window/frame. This is not allowed by the geometry manager. So you will need to decide on either grid() or pack() for all your needs in each window/frame. For now I changed TextArea.grid() to TextArea.pack() to get your code to work.
Next your button command was referencing a function that was after the command. This does not work outside of a class so you will need to move your callback() function above your AnalyzeButton.
Next we need to fix the indention on your callback() function. You must remember indention is very important in python and you should take care to keep your indention clean and consistent.
The last thing we needed to fix to get everything working as you were intending is to change:
text_input = Text.get()
To:
text_input = TextArea.get(1.0, END)
You were trying to call get() on the method that created the text widget and not the widget instance.
You also need to define from what part of the text widget you want to start reading data and how far you want to read through the text widget. this is done by applying 2 positions points with 1.0, END or "1.0", "end-1c" as tkinter allows for a few ways to apply these points. This will say 1.0 start at the first line at the first position on that line and END will say read until the last line of the textbox.
There may be other issues but I only fixed the problems preventing the code from working as intended. Your code modified to work below:
from tkinter import *
root = Tk()
root.title('Title')
TopFrame = Frame(root) # was missing from your code
MiddleFrame = Frame(root)
BottomFrame = Frame(root)
TopFrame.pack(side = TOP)
MiddleFrame.pack()
BottomFrame.pack(side = BOTTOM)
TextArea = Text()
ScrollBar = Scrollbar(root)
ScrollBar.config(command = TextArea.yview)
TextArea.config(height = 25, width = 70,
background = "white", yscrollcommand = ScrollBar.set)
TextArea.pack() # can't use pack and grid on the same window/frame. Changed this to pack()
ScrollBar.pack(side = RIGHT, fill = Y)
padx = 10
pady = 10
TextArea.pack(padx = padx, pady = pady)
# this function needs to be before the command that references it.
def callback():
# fixed indention
text_input = TextArea.get(1.0, END) # you need to reference that widget name not the tkinter method used to create the widget.
print(text_input)
AnalyzeButton = Button(BottomFrame, text = "Analyze", fg = "white", bg = "blue", command = callback)
AnalyzeButton.pack(fill = X, padx = padx, pady = pady)
root.mainloop()

How do I create multiple similar frames with tkinter using classes?

How would I create multiple frames that have the same widgets in Tkinter? Basically what I want to do is create 15 copies of a set of multiple frames that all contain the same widgets as shown in the image, the purpose of this program is to assist the user in sorting photographs into groups based on a specific ID supplied by the user. The radio buttons are there for the user to classify each photo, i.e front, back ,top etc..
Its not very efficient to copy the code 15 times and I want to know if it's possible to use a class to define the frame once and reuse the code for each new frame. I need to keep track of what the user does on each frame and save their selections on the radio buttons and check boxes for each frame. After all the photos have been classified by the user, a button is clicked that should then save all the photos with a new ID and also saves the info from the radio buttons into a csv file. Then the next batch of photos is loaded and the process repeats.
I have included an example of the code I used to create one of the frames, this is the code that I want to make reusable. I do not want to have to repeat it 15 times.
############################################################################
#FRAME 3
Photo_2 = Frame(master, bg = "white",relief = RIDGE, bd = 2)
Photo_2.grid(column = 2, row = 1, padx=5, pady=5)
Lbl2 = Label(Photo_2,text = 'Frame 3')
Lbl2.grid(row = 0, column = 0, columnspan = 4, pady = 5)
# Check box
varc2 = StringVar()
varc2.set(0)
Check_2 = Checkbutton(Photo_2, variable = varc2, text="Relevant?", command = lambda:Chk_Val(varc2))
Check_2.grid(row = 1,column = 0,columnspan = 4)
# Photo 1
P2 = "Photo_2.jpg"
P2 = Image.open(P2).resize((200, 200), Image.ANTIALIAS)
phot2 = ImageTk.PhotoImage(P2)
panel = Label(Photo_2, image = phot2)
panel.grid(columnspan = 3, column=1)
# Create Multiple Radio Buttons
Rad_Cont = Frame(Photo_2)
Rad_Cont.grid(column = 0, row = 2)
v2 = StringVar()
v2.set("Address")
for text,mode in RADIO:
b = Radiobutton(Rad_Cont, text=text, variable=v2,
value=mode, command = lambda:Rad_Val(v2))
b.pack()
################################################################################
Of course it is possible to create a class to represent similar objects.
Here is how I might implement what you're trying to accomplish:
import tkinter as tk
class PhotoFrame(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master, bg='white', relief='ridge', bd=2)
self.label_widget()
self.checkbox_widget()
self.photo_widget()
self.radio_widgets()
def label_widget(self):
self.title_label = tk.Frame(self, text='Frame 3') # Or Frame 1, 2 etc.
self.title_label.grid(row=0, column=0, columnspan=4, pady=5)
def checkbox_widget(self):
self.varc = tk.StringVar()
self.varc.set(0)
self.checkbox = tk.Checkbutton(self, variable=self.varc,
text='Relevant?', command=self.Chk_Val)
self.checkbox.grid(row=1, column=0, columnspan=4)
def photo_widget(self):
# ... Your code here
def radio_widgets(self):
# ... Your code here
def Chk_Val(self):
# ... Your code here
Now I tried not to give you the entire solution so you can learn and figure the rest out by yourself, but I'm sure you can see what I'm getting at in terms of using a class. Now this class PhotoFrame could be used as many times as you'd like, although please understand you'll have to configure each frame appropriately, e.g. I would omit the the text attribute assignment in the label_widget section (You don't want all of your frames titled 'Frame 3'), so in your main program logic, you'd configure each instance of a PhotoFrame object like so:
frame1 = PhotoFrame(master)
frame1.title_label.configure(text='Frame 1') # Along with any other configuration
I hope this all helps - if you are not familiar with classes check out the provided documentation, and here is a great tkinter reference: NMT tkinter

Why can't I see the rest of my buttons in Tkinter?

I'm new to tkinter and coding and I was wondering why can't I see the rest of my buttons. Can someone help me? I'm a total noob at coding.
from Tkinter import*
x=Tk()
def message():
y=Tk()
y.geometry("375x500")
label=Label(y,bg="light green")
recip=Entry(y)
recip.place(x=90, y=20, relwidth=0.7, relheight=0.04)
to=Label(y,text="To:",bg="maroon", fg="light green", )
to.place(x=15,y=20, relwidth=0.2,relheight=0.04)
chat=Entry(y)
chat.place(x=15,y=300, relwidth=0.7,relheight=0.05)
send=Button(y,text="send", bg="maroon", fg="light green")
send.place(x=275,y=300, relwidth=0.2,relheight=0.05)
q=Button(y,text="Q")
q.place(x=15,y=330)
w=Button(y,text="W")
w.place(x=35,y=330)
e=Button(y,text="E")
e.place(x=55,y=330)
r=Button(y,text="R")
r.place(x=75,y=330)
t=Button(y,text="T")
t.place(x=95,y=330)
y=Button(y,text="Y")
y.place(x=115,y=330)
u=Button(y,text="U")
u.place(x=15,y=330)
i=Button(y,text="I")
i.place(x=155,y=330)
o=Button(y,text="O")
o.place(x=175,y=330)
p=Button(y,text="P")#10
label.pack(expand=True, fill=BOTH)
y.mainloop()
button=Button(x,text="sample", command=message)
button.pack()
x.mainloop()
This is because you can't have two main windows eg. Tk(). Use Toplevel() to create another window.
Also you stick to one geometry manager eg. pack/place/grid. Change either place to pack or the other way round. Also the button p isn't placed at all. This I realise are a few side problems but they are important to fix. Look at the other two answers to fix your button.
Your problem as stated by Lafexlos, is that you are overwriting your instance of Tk() to add the buttons to.
I believe you should dynamically create your buttons instead of doing each 1 individually. And then given them a command that will return the corresponding letter.
I have used pack instead of place because I believe it suits this kind of program best for how the buttons are created. You will also need to make sure not to use grid, place and/or pack in the same Tk() instance as you'll find this causes problems for it.
import tkinter as tk
def open_message():
message = tk.Toplevel(root, bg = "light green")
message.minsize(300, 400)
keys = 'QWERTYUIOPASDFGHJKLZXCVBNM'
# frames for the keyboard
keyboard = tk.Frame(message, bg = "light green")
row1 = tk.Frame(keyboard)
row2 = tk.Frame(keyboard)
row3 = tk.Frame(keyboard)
row1.pack()
row2.pack()
row3.pack()
# Dynamically creates each button
# position of button depends on key index
# using index the button is assinged to the relative row
# this only includes letters. To add more, add to keys string
# and change the values to determine the assigned row
for idx, letter in enumerate(keys):
if idx < 10:
btn = tk.Button(row1, text=letter, command=lambda i=idx: get_letter(i))
elif idx < 19:
btn = tk.Button(row2, text=letter, command=lambda i=idx: get_letter(i))
else:
btn = tk.Button(row3, text=letter, command=lambda i=idx: get_letter(i))
btn.pack(side = tk.LEFT)
to_frame = tk.Frame(message, bg = "light green", padx = 40)
send_frame = tk.Frame(message, bg = "light green", padx = 40)
tk.Label(to_frame, text="To:", bg="maroon", fg="light green", relief = tk.RAISED).\
pack(side = tk.LEFT, ipady = 3)
recip = tk.Entry(to_frame)
recip.pack(side = tk.LEFT, fill = tk.BOTH, expand=True)
chat = tk.Entry(send_frame)
chat.pack(side = tk.LEFT, fill = tk.BOTH, expand=True)
send = tk.Button(send_frame, text="send", bg="maroon", fg="light green")
send.pack(side = tk.LEFT)
to_frame.pack(fill = tk.X, expand=True)
# I imagine your text widget would be packed here
keyboard.pack()
send_frame.pack(fill = tk.X, expand=True)
# Nested function to print values using keys
def get_letter(i):
# will print the letter depending on the button pressed
print(keys[i])
root = tk.Tk()
button = tk.Button(root,text="sample", command = open_message)
button.pack()
root.mainloop()

How can I close windows in tkinter for Python 3.4.3?

First time coding in Python with tkinter; online searches and the manual have not worked (for me).
Below is an example of the issue:
(I would put an image, but I'm sub 10 reputation so I cannot).
I have a definition that loads the other parts of the system named Load_Main_Menu. You can access the food menu, drink menu, etc from here. For ease of use I have edited it so that you don't have to write out code to get it to work.
I have a definition that displays the main menu, buttons the user can use to get into other parts of the system. I've reduced the code I already have so you can copy and paste it and see what I mean:
import tkinter#Imports Tkinter. Used for the user interface.
from tkinter import *#Imports the package tkinter for the user interface.
from tkinter import ttk#Imports the ttk module, used in the Food/Drink menus.
root = Tk()
tree = ttk.Treeview(root)
def Load_Main_Menu():#Loads the main menu (Food, Drink, Exit, etc.)
#Positions the buttons by setting topFrame as the top (re:First), bottomFrame as
#bottom (re: Last) and then the other buttons fill the space between
Top_Frame = Frame(root)
Top_Frame.pack()
Bottom_Frame = Frame(root)
Bottom_Frame.pack(side = BOTTOM)
#This chunk of code creates the buttons, sets the text, links it to the
#function and also decides it's colour.
Menu_Food_Button = Button(Top_Frame, text = "Food", command = Food_Button, fg = 'Blue')
Menu_Stock_Button = Button(Top_Frame, text = "Stock Control", command = Stock_Control_Button, fg = 'Green')
#The code below determines which side of the parent widget packs against.
Menu_Food_Button.pack(side=LEFT)
Menu_Stock_Button.pack(side=LEFT)
root.mainloop()
def Food_Menu():
tree['columns'] = ('Price', 'Qty', 'Print Receipt')#additional columns after the default '#0'
tree.column('Price', stretch = 0, width = 100, anchor = E)#Price Column, tkinter.E aligns contents to the "east"
tree.column('Qty', stretch = 0, width = 100, anchor = E)#Quantity Column
tree.column('Print Receipt', stretch = 0, width = 100, anchor = E)#Print Receipt Column
tree.heading('#0', text = "Item")# default column responsible for tree mechanics
tree.heading('Price', text = "£")
tree.heading('Qty', text = "Quantity")
tree.heading('Print Receipt', text = "Get Receipt")
tree.insert('', 0, 'Print_Receipt', text = "Get Receipt")#
tree.insert('', 0, '_Chips_', text = "Chips", values = (4.99, 133))#Parent, text goes to '#0', values go to tree['columns']
tree.insert('_Chips_', 0, text = "Add to Order")#Child
tree.pack()
root.mainloop()
def Stock_Menu():#Loads the main menu (Food, Drink, Exit, etc.)
#Positions the buttons by setting topFrame as the top (re:First), bottomFrame as
#bottom (re: Last) and then the other buttons fill the space between
Top_Frame = Frame(root)
Top_Frame.pack()
Bottom_Frame = Frame(root)
Bottom_Frame.pack(side = BOTTOM)
#This chunk of code creates the buttons, sets the text, links it to the
#function and also decides it's colour.
Stock_Exit_Button = Button(Top_Frame, text = "Exit", command = Return_To_Main_Menu, fg = 'Green')
#The code below determines which side of the parent widget packs against.
Stock_Exit_Button.pack(side=LEFT)
root.mainloop()
def Food_Button():#This displays the food menu.
Food_Menu()
def Stock_Control_Button():#This displays the stock options
Stock_Menu()
def Return_To_Main_Menu():#Loads the main menu
Load_Main_Menu()
Load_Main_Menu()
So, if you've C&P that you'll see a window come up with two buttons, Food and Stock.
Click Food,
Click Stock,
Click Exit,
Click Stock,
Click Exit.
You should see the issue. I want to be able to close the window 'behind me' so they don't pile up because it's a bug that kind of needs to be fixed, but I don't know how.
I've looked online, as I've said, and come up with nothing.
I fixed the indentation and root.mainloop() suggestions by the other answers. If you don't need the tree to be shown anymore, you could use tree.destroy(). However, I think it's more appropriate to use tree.pack_forget() in this case as suggested by this answer here.
I added the tree.pack_forget() to your Return_To_Main_Menu() function and removed the original Load_Main_Menu() call since it would create two new Food and Stock buttons.. I removed the comments from the original post just for easier posting.
import tkinter
from tkinter import *
from tkinter import ttk
root = Tk()
tree = ttk.Treeview(root)
def Load_Main_Menu():
Top_Frame = Frame(root)
Top_Frame.pack()
Bottom_Frame = Frame(root)
Bottom_Frame.pack(side = BOTTOM)
Menu_Food_Button = Button(Top_Frame, text = "Food", command = Food_Button, fg = 'Blue')
Menu_Stock_Button = Button(Top_Frame, text = "Stock Control", command = Stock_Control_Button, fg = 'Green')
Menu_Food_Button.pack(side=LEFT)
Menu_Stock_Button.pack(side=LEFT)
root.mainloop()
def Food_Menu():
tree['columns'] = ('Price', 'Qty', 'Print Receipt')
tree.column('Price', stretch = 0, width = 100, anchor = E)
tree.column('Qty', stretch = 0, width = 100, anchor = E)
tree.column('Print Receipt', stretch = 0, width = 100, anchor = E)
tree.heading('#0', text = "Item")
tree.heading('Price', text = "£")
tree.heading('Qty', text = "Quantity")
tree.heading('Print Receipt', text = "Get Receipt")
tree.insert('', 0, 'Print_Receipt', text = "Get Receipt")
tree.insert('', 0, '_Chips_', text = "Chips", values = (4.99, 133))
tree.insert('_Chips_', 0, text = "Add to Order")#Child
tree.pack()
def Stock_Menu():
Top_Frame = Frame(root)
Top_Frame.pack()
Bottom_Frame = Frame(root)
Bottom_Frame.pack(side = BOTTOM)
Stock_Exit_Button = Button(Top_Frame, text = "Exit", command = Return_To_Main_Menu, fg = 'Green')
Stock_Exit_Button.pack(side=LEFT)
def Food_Button():
Food_Menu()
def Stock_Control_Button():
Stock_Menu()
def Return_To_Main_Menu():
tree.pack_forget()
Load_Main_Menu()

Categories