I have three widgets (shown below) - a label, an entry, and a button.
I'm hoping to accept user input of an integer when the button is clicked.
I get the following error: ERROR RESOLVED!
I was making this too complicated. Success is Mine!!!
I believe that my problem is related to this:
Stack Overflow Post on get()
I chopped it down to the minimum amount of code possible to achieve this before posting.
Any Ideas or obvious failures you see would be greatly appreciated.
Screenshot of screen #1 (should eventually progress to screen #2 after all the players are entered. Screen #2 is where the game will begin.):
Working Code:
from tkinter import *
import tkinter
from PIL import Image, ImageTk
# The next 2 imports override the basic Tk widgets
# widget options that use fg and bg will no longer work.
# Must use, instead, ttk.Style class
from tkinter import ttk
from tkinter.ttk import *
# See docs.python.org/8/library/tkinter.ttk.html
# Class Based Windows
# youtube.com/watch?v=RkaekNkIKNY
# Tkinter - GUI Eaxample Multiple Display Frames
# youtube.com/watch?v=KdoOm3xo8X0
def main():
root = tkinter.Tk()
window1 = Window(root, "Play_a_game_of_Pig", "969x690", "someMessage",
"pig.ico", 0)
return None
class Window:
number_of_players = 0
# int player_names[] = new player_names[number_of_players]
def __init__(self, root, title, geometry, message, iconbitmap,
playerNumber):
self.root = root # this is the instance variable for the entire window
self.root.title(title)
self.root.geometry(geometry)
self.root.iconbitmap(iconbitmap)
self.number_of_players = playerNumber
# Make the window show up in center of screen and not be ccovered up by anything else.
# self.root.eval('tk::PlaceWindow %s center' % self.root.wininfo_toplevel())
# this is an important line.
# self.root.mainloop()
def addPlayers():
"""Allows user to input the number of and names of all game players"""
# stackoverflow.com/questions/12169258/should-i-use-entrys-get-or-its-textvariables-for-tkinter-in-python
print("\n initial # of players = " + str(self.number_of_players))
# Collects user input from the entry and turns it into an int
# user_input_number_of_players.set(int(str(entry_player_number.get("1.0", 'end-1c'))))
try:
user_input_number_of_players = int(entry_player_number.get())
print("Inside try block, user_input = ")
print(user_input_number_of_players)
self.number_of_players = user_input_number_of_players
except ValueError:
tkinter.messagebox.showerror('Non-Integer Input', 'User MUST enter a player # greater than 1.', icon = 'error')
# tkinter.messagebox.deiconify()
# tkinter.messagebox.quit()
# tkinter.messagebox.destroy()
#user_input_number_of_players.set(int(str(entry_player_number.get("1.0", 'end-1c'))))
# Set class instance value to this input from the user
# self.number_of_players = user_input_number_of_players
print("# of players after click = " + str(self.number_of_players))
return self.number_of_players
print("# of players after click = " + str(self.number_of_players))
# Add a label
myLabel1 = tkinter.Label(self.root, text="Please Enter # of Players",
width=25)
myLabel1.config(font="Courier 14 bold")
myLabel1.grid(row=2, column=1)
# bind user input to a variable from the entry box.
# Specifies a name whose value is linked to the widget value.
user_input_number_of_players = tkinter.StringVar()
# add an entry box
entry_player_number = tkinter.Entry(self.root, width=5, borderwidth=5,
textvariable=user_input_number_of_players)
# number.set(int("0"))
entry_player_number.grid(row=2, column=3, rowspan=2)
# specify a default value inside the entry box
# entry_player_number.insert(0,int("2"))
# Add a button for adding players to the game
addPlayerButton = tkinter.ttk.Button(self.root,
text="Enter",
command=addPlayers)
addPlayerButton.grid(row=2, column=4)
self.root.mainloop()
pass
pass
main()
In Summary, I first made two screens. I'm hoping to make these into classes. The first screen (pictured) should get input from the user. Then, the first screen should progress to the second screen.
To do this, I have used the following resources:
Resources for merging this code together into Multiple class based windows and the backend:
Python: Tkinter Class Based Windows
Tkinter - GUI Example Multiple Display Frames
Related
I have a small script which is organized in 3 frames:
1 in the first row
1 in the second row Left
1 in the second row right
I press the button in the first row frame and hand over the input value to the Label in the second row in the left.
Here my code:
import tkinter as tk
# Create Window
root = tk.Tk()
# Define String Variable
Name = tk.StringVar()
# Organize root window in 3 frames
EntryFrame = tk.Frame(root)
MainLeftFrame = tk.Frame(root)
MainRightFrame = tk.Frame(root)
# Create Buttons, Entry and Labels
NameLabel = tk.Label(MainLeftFrame, textvariable=Name)
InputName = tk.Entry(EntryFrame, width=20,bg='yellow')
SubmitButton = tk.Button(EntryFrame, text='Submit', command=lambda:action())
# Define what happens when press button reset
def reset():
MainLeftFrame.forget()
MainRightFrame.forget()
EntryFrame.pack()
# Define what happens when button is pressed
def action():
Name.set(InputName.get())
ResetButton = tk.Button(MainRightFrame, text='Reset', command=lambda: reset())
ResetButton.pack()
Placeholder = tk.Label(MainRightFrame, text="place holder")
Placeholder.pack(side="top")
EntryFrame.forget()
# Pack Widgets
EntryFrame.pack(side='top')
MainLeftFrame.pack(side='left')
MainRightFrame.pack(side='right')
InputName.pack()
SubmitButton.pack()
NameLabel.pack()
#mainloop
root.mainloop()
Now to my question:
When I press the "Submit" Button for the Second time (after pressing Reset Button) nothing is happening :(
Thanks in advance!
The reason of your program not working is that, after using forget on the MainLeftFrame and MainRightFrame you aren't packing them again when the action function is called. Adding these 2 lines of code in action function should make it work. BUT
MainLeftFrame.pack()
MainRightFrame.pack()
That's not the only issue, defining new widgets every time the function is called and packing them will additively increase the same widget set over and over again. To avoid this, you would have to predefine them and them perform forget and repacking. But a better thing to do would be to have a dedicated frame for them, so that it becomes easy for you to toggle. I have tried rewriting your script, let me know if this is what you wanted.
from tkinter import *
def reset():
entry_frame.pack()
main_frame.pack_forget()
def submit():
entry_frame.pack_forget()
main_frame.pack()
name.set(name_entry.get())
root=Tk()
entry_frame=Frame(root)
entry_frame.pack()
name_entry=Entry(entry_frame)
name_entry.pack(side='top')
submit_button=Button(entry_frame,text='Submit',command=submit)
submit_button.pack(side='top')
main_frame=Frame(root)
reset_button=Button(main_frame,text='Reset',command=reset)
reset_button.pack(side='top')
name=StringVar()
name_label=Label(main_frame,textvariable=name)
name_label.pack(side='left')
placeholer_label=Label(main_frame,text='placeholer')
placeholer_label.pack(side='right')
root.mainloop()
I am trying to create a function where if I press "1" my image changes, but if I press 1 again the image reverts back to how it was. This process should go on indefinitely.
I am able to do this if the user clicks on a button. For example, the below code simply uses config to change the function that should be run each time button is clicked.
# run this "thing" each time seat is activated
def activate(name):
name.config(image=active)
name.config(command=lambda: deactivate(name))
# run this "thing" each time seat is deactivated
def deactivate(name):
name.config(image=deactive)
name.config(command=lambda: activate(name))
x = Button(root, image=deactive, command=lambda: thing(x))
But I am unable to achieve the same if I bind a key instead:
from tkinter import *
# window config
root = Tk()
root.title("Example - on / off")
root.geometry("1080x600")
# on and off images
inactive = PhotoImage(file='green.png')
active = PhotoImage(file='red.png')
# functions to change the image using config
def something(event):
my_label.config(image=active)
def something2():
my_label.config(image=inactive)
# label which stores the image
my_label = Label(root, image=inactive)
my_label.pack()
# invisible button bind to key "1"
my_button = Button(root)
my_button.bind_all(1, something)
my_button.pack()
root.mainloop()
I would welcome any thoughts on this or an indication if I am approaching this completely wrong.
Thanks
You can simplify the logic by using itertools.cycle() and next() functions:
from tkinter import *
from itertools import cycle
root = Tk()
root.title("Example - on / off")
root.geometry("1080x600")
# on and off images
images = cycle((PhotoImage(file='green.png'), PhotoImage(file='red.png')))
def something(event):
my_label.config(image=next(images))
my_label = Label(root, image=next(images))
my_label.pack()
root.bind('1', something)
root.mainloop()
excuse the greeness. Trying to build GUI where option selected from Combobox populates text box. Nothing happening. First time programming so appreciate i have made a lot of errors here.
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
# function to display course selected
def courseDisplay():
box = course.get()
print(box)
# Create instance
win = tk.Tk()
win.resizable(130,130)
win.title("RaceCourse GUI")
# create combobox
course = tk.StringVar()
courseChosen = ttk.Combobox(win,width=60,textvariable=course,state='readonly')
courseChosen['values'] = ("Choose a course","Ascot", "Bath", "Chester")
courseChosen.grid(column=5, row=1,rowspan = 3, columnspan = 3,padx = 300, pady = 40)
courseChosen.current(0)
courseChosen.bind("<<ComboboxSelected>>", courseDisplay)
# create scrolled Text control
scrolW = 46
scrolH = 10
box = scrolledtext.ScrolledText(win, width=scrolW, height=scrolH, wrap=tk.WORD)
box.grid(column=5, row=8, columnspan=3,padx = 300,pady = 10)
# Start GUI
win.mainloop()
Since function courseDisplay is called when some event occurs on combobox (namely, when some option is selected), it should accept one variable (usually called event). So, your function should look like this:
def courseDisplay(event=None):
box = course.get()
print(box)
Of course, You should add another logic for showing test in textbox instead of print.
I want to create a reminder to remind me of the time I have chosen in the combobox by pressing the confirmed button. I put the time in a label and also create a delete button that can delete the label and button itself in the same row by a loop. It works if there's only one label, but if I increased the number of it it can only destroy the last label and button.
below is my code:
class final:
def __init__(self,app):
self.savelist=[]
self.time= StringVar()
self.timecombo = ttk.Combobox(app,textvariable=self.time)
self.timecombo.grid(row=0,column=1)
self.timecombo.config(value =('1:00','2:00','3:00','4:00','5:00','6:00','7:00','8:00','9:00','10:00','11:00','12:00'))
self.button1=Button(app,text='confirmed',command=self.save)
self.button1.grid(row=3,column=2)
***def save(self):
savetext = self.time.get()
self.savelist.append(savetext)
self.deletebutton_list = []
self.savelabel_list = []
for i in range(len(self.savelist)):
savelabel = Label(app, text=self.savelist[i])
savelabel.grid(row=4 + i, column=0)
self.savelabel_list.append((savelabel))
deletebutton = Button(app, text='delete' , command=functools.partial(self.deletelabel,idx=i))
deletebutton.grid(row=4 + i, column=1)
self.deletebutton_list.append(deletebutton)
def deletelabel(self, idx):
self.savelabel_list[idx].destroy()
self.deletebutton_list[idx].destroy()
self.savelist.remove(self.savelist[idx])
self.savelabel_list.remove(self.savelabel_list[idx])
self.deletebutton_list.remove(self.deletebutton_list[idx])***
app = Tk()
a = final(app)
app.title('things to do')
app.geometry("500x300+200+200")
app.mainloop()
I believed that there must be something wrong in the loop or the function deletelabel but I still can't fix it.
self.savelabel_list.remove(self.savelabel_list[idx])
Do not change the list. If you delete label/button #1, then label #2 becomes #1, and so when you press the button to delete label #2, it deletes label #3 because everything has moved up. Also, note that every time you call "save()" it creates a new set of widgets that overlay the old widgets, which will eventually slow down the computer. Create and grid the new time label only. Keep track of the row with a self.next_row variable (or whatever) and increment it by one each time.
This is a question that points out how usable classes are. Create a new class, with label and a close button, for each reminder.
from tkinter import *
from tkinter import ttk
class NewButton:
def __init__(self, master, label_text, this_row):
## put everything in a new frame so destroying
## one frame will destroy everything in it
self.fr=Frame(master)
self.fr.grid(row=this_row, column=1)
Label(self.fr, text=label_text).grid(row=0, column=1)
Button(self.fr, text="Close This",
command=self.fr.destroy).grid(row=0, column=2)
class Final:
def __init__(self,app):
self.app=app
self.this_row=4
self.time_var= StringVar()
self.timecombo = ttk.Combobox(app,textvariable=self.time_var)
self.timecombo.grid(row=0,column=1)
self.button1=Button(app,text='confirmed',command=self.save)
self.button1.grid(row=3,column=2)
def save(self):
save_text = self.time_var.get()
self.this_row += 1
next_button=NewButton(self.app, save_text, self.this_row)
self.time_var.set("")
app = Tk()
a = Final(app)
app.title('things to do')
app.geometry("500x300+200+200")
app.mainloop()
I got the assignment to build a program of a store.Now, the customers have to register to be able to buy, I made a main window that has buttons for each action I need to perform. When the user tries to register another window with the data needed to complete the registration appears. Now how do I store the data from the input-box into a list with a button?
Here's an example of how I'm setting each box that the user needs to fill:
var1 = StringVar()
var1.set("ID:")
label1 = Label(registerwindow,textvariable=var1,height = 2)
label1.grid(row=0,column=1)
ID=tkinter.StringVar()
box1=Entry(registerwindow,bd=4,textvariable=ID)
box.grid(row=0,column=2)
botonA= Button(registerwindow, text = "accept",command=get_data, width=5)
botonA.grid(row=6,column=2)
I tried setting the button to run a function that gets the input, but I't now working. Here's what I did
def get_data():
print (box1.get())
A few problems:
Unless you do import tkinter AND from tkinter import * - which you shouldn't; just choose one - your program will choke on either var1 = StringVar() or on ID=tkinter.StringVar().
Define the get_data() function before binding it to a Button.
You assigned box1 but then gridded box.
The following sample will get the box's contents, add it to a list, and print the list to the console every time you click "Accept." Replace the names of parent windows, grid locations of each widget, and so on to suit your program.
from tkinter import *
root = Tk()
root.wm_title("Your program")
mylist = []
def get_data(l):
l.append(box1.get())
print(l)
var1 = StringVar()
var1.set("ID:")
label1 = Label(root,textvariable=var1,height = 2)
label1.grid(row=0,column=0)
ID=StringVar()
box1=Entry(root,bd=4,textvariable=ID)
box1.grid(row=0,column=1)
botonA= Button(root, text = "accept",command=lambda: get_data(mylist), width=5)
botonA.grid(row=0,column=2)
root.mainloop()
to retrieve the value, you need to access the variable it is attached to, not the entry field on the screen:
def get_data():
print (ID.get())