I have started making a tic tac toe game for Tkinter in Python. Basically in this, it's not required to win, but to just have the basic functionality. Left-click places an "X" and right-click place an "O". Pressing "C" should clear the board and it should be done using labels and bindings. I'm having trouble displaying the "X" and "O" and I do have the board drawn. I try to click on the first row and nothing happens.
It should work like this: https://youtu.be/0x6HKuyXAuU
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Tic Tac Toe")
message = StringVar()
# This is the portion that draws out the grid used in Tic Tac Toe
label1 = ttk.Label(root, text="")
label1.grid(row=1, column=0)
label2 = ttk.Label(root, text="|")
label2.grid(row=1, column=1)
label3 = ttk.Label(root, text="")
label3.grid(row=1, column=2)
label4 = ttk.Label(root, text="|")
label4.grid(row=1, column=3)
label5 = ttk.Label(root, text="-")
label5.grid(row=4, column=1)
label6 = ttk.Label(root, text="-")
label6.grid(row=4, column=2)
label7 = ttk.Label(root, text="-")
label7.grid(row=4, column=3)
label8 = ttk.Label(root, text="-")
label8.grid(row=4, column=4)
label9 = ttk.Label(root, text=" ")
label9.grid(row=5, column=0)
label10 = ttk.Label(root, text="|")
label10.grid(row=5, column=1)
label11 = ttk.Label(root, text=" ")
label11.grid(row=5, column=2)
label12 = ttk.Label(root, text="|")
label12.grid(row=5, column=3)
label13 = ttk.Label(root, text="-")
label13.grid(row=6, column=1)
label14 = ttk.Label(root, text="-")
label14.grid(row=6, column=2)
label15 = ttk.Label(root, text="-")
label15.grid(row=6, column=3)
label16 = ttk.Label(root, text="-")
label16.grid(row=6, column=4)
label17 = ttk.Label(root, text=" ")
label17.grid(row=7, column=0)
label18 = ttk.Label(root, text="|")
label18.grid(row=7, column=1)
label19 = ttk.Label(root, text=" ")
label19.grid(row=7, column=2)
label20 = ttk.Label(root, text="|")
label20.grid(row=7, column=3)
#This is the code that draws out the O and X in this the first row
label21 = ttk.Label(root, text="")
label21.grid(row=1, column=0)
label21.bind("<Button-1>", lambda e: message.set("X"))
label21.bind("<Button-3>", lambda e: message.set("O"))
label22 = ttk.Label(root, text="")
label22.grid(row=1, column=1)
label22.bind("<Button-1>", lambda e: message.set("X"))
label22.bind("<Button-3>", lambda e: message.set("O"))
label22.grid(row=1, column=3)
label22.bind("<Button-1>", lambda e: message.set("X"))
label22.bind("<Button-3>", lambda e: message.set("O"))
root.mainloop()
Use a loop instead of creating a lot of variables and labels like that:
Here is an example which you can improve:
from tkinter import *
def updateX(event):
event.widget['text'] = 'X'
def updateO(event):
event.widget['text'] = 'O'
root = Tk()
root.title("Tic Tac Toe")
for x in range(5):
for y in range(5):
label = Label(root)
if x%2==0:
if y%2!= 0:
label['text'] = '|'
else:
label.bind('<1>', updateX)
label.bind('<Button-3>', updateO)
root.grid_columnconfigure(y, weight=1)
root.grid_rowconfigure(x, weight=1)
else:
label['text']='-'
label.grid(column=y, row=x, sticky="nsew")
root.mainloop()
your clear function can be something like this:
def clear(event):
for item in root.winfo_children():
if isinstance(item, Label) and item['text'] in ['O', 'X']:
item['text'] = ''
also don't forget to bind <c> to root.
Try this:
from functools import partial
import tkinter as tk
def change_text(button, text, event=None):
# Only if the button is empty
if button.cget("text") == "":
# Change the button's text to whatever is in the variable `text`
button.config(text=text)
def clear(event=None):
for button in buttons:
button.config(text="")
# Create the tkinter window
root = tk.Tk()
# A list to store all of the buttons
buttons = []
for i in range(9):
# Create the button:
button = tk.Label(root, text="", width=3, font=("", 15), bd=3,
relief="groove")
# 1st parameter of `partial` is the function. The rest are just arguments
command = partial(change_text, button, "X")
button.bind("<Button-1>", command) # Left click
command = partial(change_text, button, "O")
button.bind("<Button-3>", command) # Right click
# Display the button:
button.grid(row=i//3, column=i%3)
# Add the button to the list of buttons:
buttons.append(button)
clear_button = tk.Label(root, text="Clear screen", width=3, font=("", 15), bd=3,
relief="groove")
clear_button.grid(row=4, column=0, columnspan=3, sticky="news")
clear_button.bind("<Button-1>", clear) # Left click
# Run tkinter's mainloop
root.mainloop()
For more info on how partial works read this.
Your binding lambda just updates the message variable which is never used by those labels.
You can use Label widgets with border to simulate the game board. Better to use list or dict to store those labels for later reference, for example to clear the game board by pressing c character.
Below is a modified example:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Tic Tac Toe")
def set_cell(lbl, mark):
if lbl["text"] == "":
lbl["text"] = mark
def clear(event):
for lbl in cells.values():
lbl["text"] = ""
cells = {} # for storing the labels
# create the 3 x 3 board using labels
for row in range(3):
for col in range(3):
lbl = ttk.Label(root, width=3, borderwidth=1, relief="solid", font="Arial 32 bold", anchor="c")
lbl.grid(row=row, column=col, sticky="nsew")
lbl.bind("<Button-1>", lambda e: set_cell(e.widget, "X"))
lbl.bind("<Button-3>", lambda e: set_cell(e.widget, "O"))
cells[row,col] = lbl # store the label
root.bind("c", clear)
root.mainloop()
Related
I want to update getting results from an entry box in a way that when an integer enters, the equivalent rows of entry boxes appear below that. I have written the below code to make it work using a button. However, I want to make it happen automatically without a button as I entered the number, the rows update. I checked one way of doing that is using the after(). I placed after after() in the function and out of the function but it is not working.
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = IntVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
def update():
for i in range(1, n_para.get()+1):
entryX = Entry(root)
entryX.grid(row=i+1, column=0)
entryY = Entry(root)
entryY.grid(row=i+1, column=1)
entryZ = Entry(root)
entryZ.grid(row=i+1, column=2)
root.after(100, update)
root.after(1, update)
button1 = Button(root, text="update", command=update)
button1.grid(row=1, column=0)
root.mainloop()
You should try using the <KeyRelease> event bind.
import tkinter as tk
def on_focus_out(event):
label.configure(text=inputtxt.get())
root = tk.Tk()
label = tk.Label(root)
label.pack()
inputtxt = tk.Entry()
inputtxt.pack()
root.bind("<KeyRelease>", on_focus_out)
root.mainloop()
This types the text entered in real-time.
Edited Code with OP's requirement:
from tkinter import *
root = Tk()
root.geometry("400x400")
n_para = IntVar()
label1 = Label(root, text="Numeric parameters")
label1.grid(row=0, column=0)
entry1 = Entry(root, textvariable=n_para)
entry1.grid(row=0, column=1)
def upd(event):
x = entry1.get()
if not x.isnumeric():
x = 0
for i in range(1, int(x)+1):
entryX = Entry(root)
entryX.grid(row=i+1, column=0)
entryY = Entry(root)
entryY.grid(row=i+1, column=1)
entryZ = Entry(root)
entryZ.grid(row=i+1, column=2)
# root.after(100, update)
root.bind("<KeyRelease>", upd)
# button1 = Button(root, text="update", command=update)
# button1.grid(row=1, column=0)
root.mainloop()
When the "view" button is pressed, it should trigger the function solution(i) such that label should be displayed in the new window. The problem is that the window opens and the previous label is packed but the label which gets it's text from "i" does not gets packed, Is there any issue in passing the parameter.
Any help is appreciated.
root = Tk()
root.config(background = "#303939")
root.state('zoomed')
def pre():
with open("DoubtSolution.txt","r+") as f:
dousol = f.read()
dousol_lst = dousol.split("`")
k = 0
window = Tk()
window.config(background = "#303939")
window.state('zoomed')
predoubt = Label(window,
text="Previous Doubts",
fg="Cyan",
bg="#303939",
font="Helvetica 50 bold"
).grid(row=0, column=1)
def solution(text):
print(text)
window1 = Tk()
window1.config(background="#303939")
window1.state('zoomed')
sol = Label(window1,
text=text[:text.find("~")],
font=font.Font(size=20),
bg="#303939",
fg="Cyan")
sol.pack()
window1.mainloop()
for i in dousol_lst:
if i[-5:] == admno:
doubt = Label(window, text=i[i.find("]]")+2:i.find("}}}}")], font=font.Font(size=20), bg="#303939",
fg="Cyan")
doubt.grid(row=2+k, column=1, pady=10)
view = Button(
master=window,
text="View", font=font.Font(size=15, family="Helvetica"),
activebackground="White",
bg="Teal",
bd=0.8,
fg="White",
command = lambda k = k:solution(i)
)
view.grid(row=2+k, column=2, padx=20)
k=k+1
window.mainloop()
previous = Button(
master=root,
text="Previous Doubts", font="Helvetica 22 bold",
activebackground="White",
bg="Teal",
bd=0.8,
fg="White",
command = pre
).grid(row=4, column=3, padx=20)
root.mainloop()
The point of this is to press a button and have new rows appear but currently, titles appear above each new row that is added. Instead, I would like to have one row at the top of the grid with column titles. Is there a way to modify this code I already have? Later, I will be incorporating this into a larger tkinter GUI.
from tkinter import *
#------------------------------------
def addbox():
frame =Frame(root)
frame.pack()
#Item
Label(frame, text="Item").grid(row=0, column=0)
ent1 = Entry(frame, width=10)
ent1.grid(row=2, column=0)
#Day
Label(frame, text="Day").grid(row=0, column=1)
ent2 = Entry(frame, width=10)
ent2.grid(row=2, column=1)
#Code
ent3 = Entry(frame, width=10)
ent3.grid(row=2, column=2)
#Factor
ent4 = Entry(frame, width=10)
ent4.grid(row=2, column=3)
all_entries.append( (ent1, ent2, ent3, ent4) )
#Buttons.
showButton = Button(frame, text='Print', command=refresh)
addboxButton = Button(frame, text='Add Item', fg="Red", command=addbox)
#------------------------------------
def refresh():
for number, (ent1, ent2, ent3, ent4) in enumerate(all_entries):
print (number, ent1.get(), ent2.get(), ent3.get(),ent4.get())
#------------------------------------
all_entries = []
root = Tk()
addboxButton = Button(root, text='Add Instrument', fg="Red", command=addbox)
addboxButton.pack()
root.mainloop()
You can check the row count before you add the label:
if len(all_entries) < 1:
Label(frame, text="Item").grid(row=0, column=0)
I am trying to clear the items on the window without closing the root window just the items visible.While everything is deleted Some of the labels are still left.I created a button "close" to remove the items.My code is as below;
from tkinter import*
root = Tk()
root.geometry("480x480")
# Node
myLabel1 = Label(root, text=f'Node')
myLabel1.grid(row=0, column=0)
rows = [] # show the input entry boxes
for i in range(6):
# name
entry = Entry(root, width=5, bd=5)
entry.grid(row=2+i, column=0)
# x
myLabel2 = Label(root, text=f'x{i}')
myLabel2.grid(row=2+i, column=1)
entry_x = Entry(root, width=5, bd=5)
entry_x.grid(row=2+i, column=2)
# y
myLabel3 = Label(root, text=f'y{i}')
myLabel3.grid(row=2+i, column=3)
entry_y = Entry(root, width=5, bd=5)
entry_y.grid(row=2+i, column=4)
# save current input row
rows.append((entry, entry_x, entry_y))
def close():
for name,ex,ey in rows:
name.destroy()
ex.destroy()
ey.destroy()
myLabel3.destroy()
myLabel2.destroy()
myLabel1.destroy()
myButton_close.destroy()
myButton_close = Button(root, text="close",padx = 10,pady = 10, command=close)
myButton_close.grid(row=8, column=6)
root.mainloop()
where could i be going wrong?
Create a Frame to hold the widgets and then you can destroy the frame to clear the window:
from tkinter import *
root = Tk()
root.geometry("480x480")
frame = Frame(root)
frame.pack(fill=BOTH, expand=1)
# Node
myLabel1 = Label(frame, text=f'Node')
myLabel1.grid(row=0, column=0)
rows = [] # store the input entry boxes
for i in range(6):
# name
entry = Entry(frame, width=5, bd=5)
entry.grid(row=2+i, column=0)
# x
myLabel2 = Label(frame, text=f'x{i}')
myLabel2.grid(row=2+i, column=1)
entry_x = Entry(frame, width=5, bd=5)
entry_x.grid(row=2+i, column=2)
# y
myLabel3 = Label(frame, text=f'y{i}')
myLabel3.grid(row=2+i, column=3)
entry_y = Entry(frame, width=5, bd=5)
entry_y.grid(row=2+i, column=4)
# save current input row
rows.append((entry, entry_x, entry_y))
def close():
frame.destroy()
myButton_close = Button(frame, text="close", padx=10, pady=10, command=close)
myButton_close.grid(row=8, column=6)
root.mainloop()
Grouping widgets into a frame make it easier to clear certain part of the window.
You create the labels in a loop but you are not saving any reference to the labels except for the last myLabel2/3. However you can ask a widget about its children, and then destroy them all:
def close():
for widget in root.winfo_children():
widget.destroy()
I am making a quiz in python using the tkinter module and I am stuck on how to create a button that checks to see if the answer is correct or not. But I would put it in a procedure however the question is already in one.
import tkinter as tk
window = tk.Tk()
window.title("6 Questions")
window.geometry("500x150")
score = 0
def inst():
t = tk.Label(window, text="All you need to do is just answer each question with either a '1, 2, 3' or the actual word.")
t.pack()
def start():
root = tk.Tk()
root.title("question 1")
q = tk.Label(root, text="what type of input holds whole numbers")
q.pack()
a = tk.Label(root, text="1.) int")
a.pack()
b = tk.Label(root, text="2.) string")
b.pack()
c = tk.Label(root, text="3.) float")
c.pack()
ans = tk.Entry(root, width=40)
ans.pack()
#here is the button I want to verify the answer
sub = tk.Button(root, text="Submit")
sub.pack()
greet = tk.Label(window, text="Welcome to the 6 Question Quiz.")
greet.pack()
start = tk.Button(window, command=start, text="Start")
start.pack()
instr = tk.Button(window, text="Instructions", command=inst)
instr.pack()
end = tk.Button(window, text="Exit", command=exit)
end.pack()
Create a function that opens when the submit button is clicked and create RadioButtons rather than Labels.
Like this:
def gettingDecision():
if var.get() is 'True':
messagebox.showinfo('Congrats', message='You Are Correct.Score is {}'.format(score))
else:
messagebox.showinfo('Lose', message='You Are Wrong.')
Question1 = ttk.Label(frame1, text='Q.1.Where does a computer add and compare data ?')
Question1.grid(row=1, column=0, sticky=W)
var = StringVar()
Q1A = ttk.Radiobutton(frame1, text='[A] Hard disk', variable=var, value='False1')
Q1A.grid(row=2, column=0, sticky=W)
Q1B = ttk.Radiobutton(frame1, text='[B] Floppy disk', variable=var, value='False2')
Q1B.grid(row=3, column=0, sticky=W)
Q1C = ttk.Radiobutton(frame1, text='[C] CPU chip', variable=var, value='True')
Q1C.grid(row=4, column=0, sticky=W)
Q1D = ttk.Radiobutton(frame1, text='[D] Memory chip', variable=var, value='False3')
Q1D.grid(row=5, column=0, sticky=W)
submit = ttk.Button(frame1, text='Submit', command=gettingDecision)
submit.grid()
Please note that, ideal way to go would be using classes.
You can define a function, inside of a function.
import tkinter as tk
window = tk.Tk()
window.title("6 Questions")
window.geometry("500x150")
score = 0
def inst():
t = tk.Label(window, text="All you need to do is just answer each question with either a '1, 2, 3' or the actual word.")
t.pack()
def start():
def submit():
print (ans.get())
#or do whatever you like with this
root = tk.Tk()
root.title("question 1")
q = tk.Label(root, text="what type of input holds whole numbers")
q.pack()
a = tk.Label(root, text="1.) int")
a.pack()
b = tk.Label(root, text="2.) string")
b.pack()
c = tk.Label(root, text="3.) float")
c.pack()
ans = tk.Entry(root, width=40)
ans.pack()
#here is the button I want to verify the answer
sub = tk.Button(root, text="Submit", command=submit)
sub.pack()
greet = tk.Label(window, text="Welcome to the 6 Question Quiz.")
greet.pack()
startButton = tk.Button(window, command=start, text="Start")
startButton.pack()
instr = tk.Button(window, text="Instructions", command=inst)
instr.pack()
end = tk.Button(window, text="Exit", command=window.destroy)
end.pack()
window.mainloop()