Tkinter Confirmation buttons and game GUI (splice(?)) - python

Never used Tkinter before, and I'm not quite sure what to do with it or how it works. Windows IDLE Shell.
import time
from tkinter import *
input("Press enter to begin...")
print ("Welcome Traveller!")
time.sleep(1)
def name_identify():
print ()
global name_input
name_input = input ("What is your name? ").lower()
name_input = name_input.title()
time.sleep(0.75)
def name_confirm():
print ()
print ("So %s is your name then?" % name_input)
time.sleep(1.5)
print ()
confirmation = input ("Are you sure? Yes or No? ").lower()
if confirmation == "Yes" or confirmation == "yes" or confirmation == "aye" or confirmation == "yay":
print("")
print ("Then %s, let your adventure begin..." % name_input)
elif confirmation == "no" or confirmation == "No" or confirmation == "nay":
name_identify()
else:
print ("Please answer with either yes or no young traveller.")
time.sleep(2)
name_confirm()
name_confirm()
name_identify()
If possible I would like to put the game into a small GUI made with Tkinter just to make the mini text adventure tests I'm making easier to navigate when people play it. As such, I wish to turn the required "yes" and "no" responses needing to be typed into buttons, so the player doesn't need to touch the keyboard to finish. Problem is, I have no idea how to get all the data into the little TKinter interface along with the buttons working how I intend.
I can create the root which holds the buttons themselves, on a very basic level(likely not even correctly), but I do not know how to link parameters and variables to the buttons, nor how to place the text into the created console. All my attempts have simply ended with loops or the console simply not opening.
from tkinter import *
def main():
root = Tk()
root.title("Tkinter Test")
root.minsize(width=200, height=120)
root.maxsize(width=400, height=240)
button = Button(root, text="This is a button!", width=20, height=5)
button.pack()
root.mainloop()
if __name__ == '__main__':
main()
I thank whoever helps in advance. Even a template to work off would be great help, as I can just customise and modify until it suits my needs, but if someone would be so kind as to make a simple template for me based on the below image I would be grateful, as I would like it to follow a simple enough flow similar to that. And sorry if the image isn't clear enough. And maybe some advice on aligning said buttons and text, if it's possible for you.

The below code shows how you can achieve this and is commented to explain:
from tkinter import *
root = Tk() #declares that the main window belongs to root
frame = Frame(root) #creates a frame inside the main window so that we don't destroy the actual window when we refresh
def command1(frame):
frame.destroy() #destroys the frame and everything in it
frame = Frame(root) #new frame
label1 = Label(frame, text="What is your name?") #text label
entry1 = Entry(frame) #entry widget
button1 = Button(frame, text="Ok", command=lambda:command2(frame, entry1)) #continue button
frame.pack() #packs item
label1.pack() #packs item
entry1.pack() #packs item
button1.pack() #packs item
def command2(frame, entry1):
var = entry1.get() #gets the text entered in the last phase and stores it before the item is destroyed
frame.destroy() #destroys the frame and everything in it
frame = Frame(root) #new frame
label1 = Label(frame, text="So %s is your name then? Are you sure?" % var) #text label
button1 = Button(frame, text="Yes", command=lambda:command3(frame, var)) #yes button
button2 = Button(frame, text="No", command=lambda:command1(frame)) #no button
frame.pack() #packs item
label1.pack() #packs item
button1.pack() #packs item
button2.pack() #packs item
def command3(frame, var):
frame.destroy() #destroys the frame and everything in it
frame = Frame(root) #new frame
label1 = Label(frame, text="Then %s, let your adventure begin..." % var) #text label
frame.pack() #packs item
label1.pack() #packs item
label1 = Label(frame, text="Press below to begin...") #text label
button1 = Button(frame, text="Begin", command=lambda:command1(frame)) #begin button
frame.pack() #packs item
label1.pack() #packs item
button1.pack() #packs item
root.mainloop() #starts event loop
I would still recommend http://effbot.org/tkinterbook/ as a starting point for tkinter.
The below shows how you can align the two buttons next to each other, the code is commented to show where it varies from the original:
from tkinter import *
root = Tk()
frame = Frame(root)
def command1(frame):
frame.destroy()
frame = Frame(root)
label1 = Label(frame, text="What is your name?")
entry1 = Entry(frame)
button1 = Button(frame, text="Ok", command=lambda:command2(frame, entry1))
frame.pack()
label1.pack()
entry1.pack()
button1.pack()
def command2(frame, entry1):
var = entry1.get()
frame.destroy()
frame = Frame(root)
frame1 = Frame(frame) #creates lower frame
label1 = Label(frame, text="So %s is your name then? Are you sure?" % var)
button1 = Button(frame1, text="Yes", command=lambda:command3(frame, var)) #this button is now in the lower frame
button2 = Button(frame1, text="No", command=lambda:command1(frame)) #this button is now in the lower frame
frame.pack()
frame1.pack(side="bottom") #packs lower frame
label1.pack()
button1.pack(side="left") #packs button left
button2.pack(side="right") #packs button right
def command3(frame, var):
frame.destroy()
frame = Frame(root)
frame1 = Frame(frame)
label1 = Label(frame, text="Then %s, let your adventure begin..." % var)
frame.pack()
label1.pack()
label1 = Label(frame, text="Press below to begin...")
button1 = Button(frame, text="Begin", command=lambda:command1(frame))
frame.pack()
label1.pack()
button1.pack()
root.mainloop()

Related

Tkinter Entry widget is returning an empty list when used in a toplevel window

I am attempting to open a new window when the New Window button is pressed in the main "root" window. This currently works and does indeed open a second window. In the second window I want to ask the user for an input and then this input will be turned into a list of strings.
An example input would be "Amy, Bob, Carl". The expected output would be ['Amy', 'Bob', 'Carl'] but currently the program just returns [''].
My current code is:
from tkinter import *
root = Tk()
root.title("Welcome screen")
root.geometry("300x300")
def open_new_window():
top = Toplevel()
top.title("second window")
entities = Entry(top)
entries = entities.get().split(", ")
entities.pack()
entities.focus_set()
print(entries)
sub_button = Button(top, text="Submit", command= ?)
sub_button.pack(pady=20)
close_btn = Button(top, text="Close", command=top.destroy)
close_btn.pack()
open_button = Button(root, text="New Window", command=open_new_window)
open_button.pack(pady=20)
exit_button = Button(root, text="Close", command=root.destroy)
exit_button.pack(pady=20)
root.mainloop()
I am new to Tkinter and I am unsure why this is happening. I'm sure it's a simple silly mistake but I can't find where I am going wrong. I also am unsure as to whether I need a Submit button as I don't know what command should be passed to it.
Any advice is appreciated. Please let me know if any additional information is required.
First, we will understand why you got a empty list: your code is executed sequentially, so when you do the entities.get() you haven't yet written anything nor pressed "Submit", i.e., you want to read the entry box once you press the "Submit", not earlier, for that reason, you have the command = ?.
As I am aware, you have mainly 2 options:
Get the text from the button itself
Create a variable linked to the entry box and read this
Method 1: read the data from the entry
from tkinter import *
root = Tk()
root.title("Welcome screen")
root.geometry("300x300")
def do_stuff(entry):
print(entry.get())
def open_new_window():
top = Toplevel()
top.title("second window")
entities = Entry(top)
entities.pack()
entities.focus_set()
sub_button = Button(top, text="Submit", command= lambda: do_stuff(entities))
sub_button.pack(pady=20)
close_btn = Button(top, text="Close", command=top.destroy)
close_btn.pack()
open_button = Button(root, text="New Window", command=open_new_window)
open_button.pack(pady=20)
exit_button = Button(root, text="Close", command=root.destroy)
exit_button.pack(pady=20)
root.mainloop()
Method 2: link a variable
from tkinter import *
root = Tk()
root.title("Welcome screen")
root.geometry("300x300")
def do_stuff(text_entry):
print(text_entry.get())
def open_new_window():
top = Toplevel()
top.title("second window")
text_entry = StringVar()
entities = Entry(top, textvariable = text_entry)
entities.pack()
entities.focus_set()
sub_button = Button(top, text="Submit", command= lambda: do_stuff(text_entry))
sub_button.pack(pady=20)
close_btn = Button(top, text="Close", command=top.destroy)
close_btn.pack()
open_button = Button(root, text="New Window", command=open_new_window)
open_button.pack(pady=20)
exit_button = Button(root, text="Close", command=root.destroy)
exit_button.pack(pady=20)
root.mainloop()
The main advantage in this last approach is that you can play with the text before and after the entry is built.

How can I make a Print and Clear button GUI which prints and clears a Label in Python?

I am a beginner programmer and I am trying to solve a very small problem.
I want to make a GUI which has 2 buttons namely, Print and Clear.
I tried my best but cant solve it yet. Wanted Help....
Here is my code( ignore neatness ):
from tkinter import *
main = Tk()
main.geometry("200x200")
global buttonstate
def change():
buttonstate = True
return buttonstate
button1 = Button(main, text="Button", command=change)
if buttonstate==True:
global label
label = Label(main, text= "this works")
elif buttonstate==False:
pass
button2 = Button(main, text="clear", command=lambda: label.pack_forget())
button1.pack()
button2.pack()
label.pack()
main.mainloop()
I am unable to do all the thing in a loop and also to print the statement when the button is clicked...
Thanks.
Here is the GUI with two buttons and a label,
from tkinter import *
main = Tk()
# label where you will print text
text_label = Label(main, width=20)
text_label.grid(row=0, columnspan=2, padx=8, pady=4)
# function to print text in label
def print_text():
# the message you want to display
your_text = "Some message.."
# update the text of your label with your message
text_label.config(text=your_text)
# function to clear text in label
def clear_text():
# clear the label text by passing an empty string
text_label.config(text='')
# print button
# command option is the function that you want to call when the button is pressed
print_btn = Button(main, text='Print', command=print_text, width=8)
print_btn.grid(row=1, column=0, padx=8, pady=4)
# clear button
clear_btn = Button(main, text='Clear', command=clear_text, width=8)
clear_btn.grid(row=1, column=1, padx=8, pady=4)
main.mainloop()
Output GUI
Click Print button to print message
Click Clear button to clear message
I have commented each segment of the code. Hope you understand

Button on far right despite only being in 2nd (1) column

import random
from tkinter import *
root = Tk()
FirstPageFrame = Frame(root) # CREATES FIRST PAGE FRAME
FirstPageFrame.pack() # CREATES FIRST PAGE FRAME
RolltheDiceTitle = Label(FirstPageFrame, text="Roll the Dice")
RolltheDiceTitle.config(font=("Courier", 30))
RolltheDiceTitle.grid()
LoginRegisterWelcomeMessage = Label(FirstPageFrame, text="Welcome to the Roll the Dice Game.")
LoginRegisterWelcomeMessage.grid(row=1, padx=10, pady=10)
DiceImage = PhotoImage(file="dice.png")
DiceImageLabel = Label(FirstPageFrame, image=DiceImage)
DiceImageLabel.grid()
registerButton = Button(FirstPageFrame, text="Register", fg="orange") # CREATES REGISTER BUTTON
registerButton.grid(row=4, padx=10, pady=10)
loginButton = Button(FirstPageFrame, text="Login", fg="Green") # CREATES LOGIN BUTTON
loginButton.grid(row=4, column=1, padx=10, pady=10)
root.mainloop() # Continues tkinter window loop so program does not close
I'm trying to have both the register and login buttons in the middle of the window (where register button is located) but side by side, not stacked.
Apologies for any incorrect formatting with this question or any easy fixes with the code. I'm super new to Tkinter and trying to figure stuff out :)
When you put the Login button in column 1 all the other widgets are in column 0, so the Login button will be displayed on the far right.
One solution is to create a contaner frame for the buttons which can be placed in the center of column 0, and then place the buttons in culmns in that frame.
I have stated row and colun explicitly for all widgets which makes it easier to follow, and also assigned a bg color to the buttonFrame so you can see where it goes.
import random
from tkinter import *
root = Tk()
FirstPageFrame = Frame(root) # CREATES FIRST PAGE FRAME
FirstPageFrame.pack() # CREATES FIRST PAGE FRAME
RolltheDiceTitle = Label(FirstPageFrame, text="Roll the Dice")
RolltheDiceTitle.config(font=("Courier", 30))
RolltheDiceTitle.grid(row=0, column=0)
LoginRegisterWelcomeMessage = Label(FirstPageFrame, text="Welcome to the Roll the Dice Game.")
LoginRegisterWelcomeMessage.grid(row=1, column=0, padx=10, pady=10)
DiceImage = PhotoImage(file="dice.png")
DiceImageLabel = Label(FirstPageFrame, image=DiceImage)
DiceImageLabel.grid(row=2, column=0)
# Here is the container frame for the buttons.
buttonFrame = Frame(FirstPageFrame, bg='tan') # bg color to indicate
buttonFrame.grid(row=3) # position and extent of frame
registerButton = Button(buttonFrame, text="Register", fg="orange") # CREATES REGISTER BUTTON
registerButton.grid(row=0, column=0, padx=10, pady=10)
loginButton = Button(buttonFrame, text="Login", fg="Green") # CREATES LOGIN BUTTON
loginButton.grid(row=0, column=1, padx=10, pady=10)
root.mainloop() # Continues tkinter window loop so program does not close

How do I use a Tkinter Button for two states?

I have the following code, which causes a color/text change when a Tkinter button is clicked. I would like to revert to the original color/text when the button is clicked a second time.
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
I want something that behaves a little more like a toggle, but I would like the look of a button.
Help gratefully received.
You will need an if block to choose what to do. You can make another flag variable to keep track of the state, or just use the current Label or Button text:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
def clicked_rf1():
if btn_rf1['text'] == "Start":
btn_rf1.configure(text="Stop")
lbl_rf1.configure(text=" ON ", bg="green")
else:
btn_rf1.configure(text="Start")
lbl_rf1.configure(text=" OFF ", bg="red")
btn_rf1 = Button(window, text="Start", command=clicked_rf1)
btn_rf1.grid(column=1, row=1)
lbl_rf1 = Label(window, text=" OFF ", bg="red")
lbl_rf1.grid(column=2, row=1)
window.mainloop()
This would be an ideal place to make a custom Button subclass, so you could have many of these in your program:
from Tkinter import *
window = Tk()
window.title("Start/Stop Button")
window.geometry('200x100')
class Christina(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
self.btn = Button(self, text="Start", command=self.clicked)
self.btn.grid(column=0, row=0)
self.lbl = Label(self, text=" OFF ", bg="red")
self.lbl.grid(column=1, row=0)
def clicked(self):
if self.btn['text'] == "Start":
self.btn.configure(text="Stop")
self.lbl.configure(text=" ON ", bg="green")
else:
self.btn.configure(text="Start")
self.lbl.configure(text=" OFF ", bg="red")
btn1 = Christina(window)
btn1.grid()
btn2 = Christina(window)
btn2.grid()
btn3 = Christina(window)
btn3.grid()
window.mainloop()
If you want a toggle, you can use the checkbutton without an indicator. It has options for the color in the selected and deselected state, and you can tie the value and the label together so that the label changes when you toggle the button.
Like any button, you can tie a command to it. The command can check the value of the variable to determine whether it should do the "on" function or the "off" function.
Here's a simple example:
import Tkinter as tk
def toggle():
if var.get() == "ON":
print("turning on...")
else:
print("turning off...")
root = tk.Tk()
var = tk.StringVar()
toggle = tk.Checkbutton(root, onvalue="ON", offvalue="OFF", width=4,
indicatoron=False,
variable=var, textvariable=var,
selectcolor="green", background="red",
command=toggle)
var.set("OFF")
toggle.pack()
root.mainloop()
Another approach might be to put the "pile of code" to run into different function, collect those in an iterator, and then get the next function from that iterator and execute it:
def bunchofcode():
print("foo")
def somethingelse():
print("bar")
whattodo = iter([bunchofcode, somethingelse])
def clicked_rf1():
try:
next(whattodo)()
except StopIteration:
print("nothing to do")
Or for cyclic behaviour:
from itertools import cycle
whattodo = cycle([bunchofcode, somethingelse])
For a two-state toggle button, you could also use a dict to map the current state to the next. You could also use the button's relief to mark the state.
def clicked_rf1():
transition = {"raised": "sunken", "sunken": "raised"}
btn_rf1["relief"] = transition[btn_rf1["relief"]]

How can I hide the main tkinter program window whilst logging in using another Toplevel window?

I am practicing with a simple login system. I have a main program window which I wish to hide whilst a login window appears. When the correct password is entered I want the login window to destroy and the main window to reappear (deiconify).
The main program code (LibrarySQL.py):
from tkinter import *
import libraryentrySQL, librarydatabase, login
import sqlite3
import os
def click():
entered_text = entry.get() #collect text from text entry box
output.delete(0.0,END) #clears text box - start clearing from 0.0 (from line 0) to END (after last character)
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("SELECT definition FROM Dictionary WHERE word = ?", (entered_text,))
for row in c:
definition = row[0]
break
else:
definition = "No word found in dictionary, try again!"
output.insert(END, definition) #this inserts the contents of variable 'definition' at the beginning (END) - because it was cleared before, END is the at the start
def clickentry(): #this function is run when the 2nd button (entry is pressed)
def definition_submitted(word, definition):
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("INSERT INTO Dictionary VALUES (?, ?)", (word, definition))
new_db.commit()
new_db.close()
definition_window = libraryentrySQL.DefinitionWindow(window, definition_submitted) #this creates the object 'definition window' and passes to it 'the window variable'
#so that it can have a canvas
def clickdataview():
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute("SELECT * FROM Dictionary")
data = c.fetchall()
count = len(data)
database_window = librarydatabase.DatabaseWindow(window, count, data)
new_db.close()
def login_window(window):
log = login.Login(window)
window = Tk()
login_window(window)
window.withdraw()
window.deiconify()
window.title("My Little Dictionary")
#Create the Label
Label(window, text="Enter the word you want defining:").grid(row=0, column=0, sticky=W)
#create entry box
entry=Entry(window, width=20, bg="light green")
entry.grid(row=1, column=0, sticky=W)
#create submit button
Button(window, text="Submit", width=5, command=click).grid(row=2, column=0, sticky=W)
#create second label
Label(window, text="\nDefinition").grid(row=3, column=0, sticky=W)
#create text box
output=Text(window, width=75, height=6, wrap=WORD, background="light green")
output.grid(row=4, column=0, sticky=W)
#create submit button to open enter new definition window
Button(window, text="Enter a New Definition", width=20, command=clickentry).grid(row=5, column=0, sticky=W)
#create open database button to open database view window
Button(window, text="View Dictionary", width=20, command=clickdataview).grid(row=6, column=0, sticky=W)
#Create the Dictionary.db if not already present
if not os.path.isfile("dictionary.db"):
new_db = sqlite3.connect('dictionary.db')
c=new_db.cursor()
c.execute('''CREATE TABLE Dictionary
(word text,
definition text)''')
c.execute('''INSERT INTO Dictionary VALUES
('Algorithm', 'Step by step instructions to complete a task')''')
new_db.commit()
new_db.close()
window.mainloop()
The Login class code (login.py):
from tkinter import *
class Login(Toplevel):
def __init__(self, window):
Toplevel.__init__(self, window)
self.title("Current Library")
Label(self, text="Log in to use this program:").grid(row=0, column=0, sticky=W)
self.userbox=Entry(self, width=20, bg="light green")
self.userbox.grid(row=1, column=0, sticky=W)
self.passbox=Entry(self, width=20, bg="light green")
self.passbox.grid(row=2, column=0, sticky=W)
Button(self, text="Submit", width=5, command=self.clicked).grid(row=3, column=0, sticky=W)
def clicked(self):
username = self.userbox.get()
password = self.passbox.get()
if password == "password":
self.correct = True
self.destroy()
else:
pass
I know exactly where the problem is: the main window hides then reappears instantly due to the order of instructions in the mainloop.
How therefore, can I get the main to hide and the login window to appear UNTIL the password is entered correctly? After which I wish the login window to destroy and the main to reappear?
Any help would be gratefully received.
You can modify the clicked() method of your Toplevel instance to call deiconify, that way it's only called when the Toplevel is destroyed.
class Login(Toplevel):
def __init__(self, window):
Toplevel.__init__(self, window)
# this is the parent/root window
self.window = window
Button(self, text='show main', command=self.click).pack()
def click(self):
self.destroy() # destroy the toplevel
self.window.deiconify() # and restore the root window
And in your main module:
root = Tk()
# make the login instance
Login(root)
# withdraw the main window
root.withdraw()
# set up widgets in main window
...
mainloop()

Categories

Resources