Can someone help me add a reset button oroption?
I am a beginner and I could not find how to do this, hope somebody can help me. It's a python card matching game, it's a school project so it doesn't need to be the best looking, I just need to put a option to restart the game
import random
import time
from tkinter import Tk, Button, DISABLED, messagebox
def close_window(self):
root.destroy()
def show_symbol(x, y):
global first
global previousX, previousY
global moves
global pairs
buttons[x, y]['text'] = button_symbols[x, y]
buttons[x, y].update_idletasks()
if first:
previousX = x
previousY = y
first = False
moves = moves + 1
elif previousX != x or previousY != y:
if buttons[previousX, previousY]['text'] != buttons[x, y]['text']:
time.sleep(0.5)
buttons[previousX, previousY]['text'] = ''
buttons[x, y]['text'] = ''
else:
buttons[previousX, previousY]['command'] = DISABLED
buttons[x, y]['command'] = DISABLED
pairs = pairs + 1
if pairs == len(buttons) / 2:
messagebox.showinfo('Matching', 'Broj poteza: ' + str(moves))
first = True
root = Tk()
root.title('Igra Memorije')
root.resizable(width=False, height=False)
buttons = {}
first = True
previousX = 0
previousY = 0
moves = 0
pairs = 0
button_symbols = {}
symbols = [u'\u2702', u'\u2702', u'\u2705', u'\u2705', u'\u2708', u'\u2708',
u'\u2709', u'\u2709', u'\u270A', u'\u270A', u'\u270B', u'\u270B',
u'\u270C', u'\u270C', u'\u270F', u'\u270F', u'\u2712', u'\u2712',
u'\u2714', u'\u2714', u'\u2716', u'\u2716', u'\u2728', u'\u2728',
]
random.shuffle(symbols)
for x in range(6):
for y in range(4):
button = Button(command=lambda x=x, y=y: show_symbol(x, y), width=5, height=3, border=2)
button.grid(column=x, row=y,padx=15,pady=20)
buttons[x, y] = button
button_symbols[x, y] = symbols.pop()
root.mainloop()
this is the code hope you dont find it messy
To keep your coding style, I'd do something like this:
import random
import time
from tkinter import Tk, Button, DISABLED, messagebox
def close_window(self):
root.destroy()
def show_symbol(x, y):
global first
global previousX, previousY
global moves
global pairs
buttons[x, y]['text'] = button_symbols[x, y]
buttons[x, y].update_idletasks()
if first:
previousX = x
previousY = y
first = False
moves = moves + 1
elif previousX != x or previousY != y:
if buttons[previousX, previousY]['text'] != buttons[x, y]['text']:
time.sleep(0.5)
buttons[previousX, previousY]['text'] = ''
buttons[x, y]['text'] = ''
else:
buttons[previousX, previousY]['command'] = DISABLED
buttons[x, y]['command'] = DISABLED
pairs = pairs + 1
if pairs == len(buttons) / 2:
messagebox.showinfo('Matching', 'Broj poteza: ' + str(moves))
for k in tuple(buttons):
buttons.pop(k).grid_forget()
Button(root, text='Restart', command=start).grid(padx=15, pady=20)
first = True
root = Tk()
root.title('Igra Memorije')
root.resizable(width=False, height=False)
buttons = {}
first = True
previousX = 0
previousY = 0
moves = 0
pairs = 0
button_symbols = {}
symbols = [u'\u2702', u'\u2702', u'\u2705', u'\u2705', u'\u2708', u'\u2708',
u'\u2709', u'\u2709', u'\u270A', u'\u270A', u'\u270B', u'\u270B',
u'\u270C', u'\u270C', u'\u270F', u'\u270F', u'\u2712', u'\u2712',
u'\u2714', u'\u2714', u'\u2716', u'\u2716', u'\u2728', u'\u2728',
]
def start():
global buttons
global symbols
global first
global previousX, previousY
global moves
global pairs
first = True
previousX = 0
previousY = 0
moves = 0
pairs = 0
random.shuffle(symbols)
for child in root.winfo_children():
child.grid_forget()
width, height = 6, 4
for x in range(width):
for y in range(height):
button = Button(root, command=lambda x=x, y=y: show_symbol(x, y), width=5, height=3, border=2)
button.grid(column=x, row=y,padx=15,pady=20)
buttons[x, y] = button
button_symbols[x, y] = symbols[x * height + y]
Button(root, text='Start', command=start).grid(padx=15,pady=20)
root.mainloop()
The things I've changed:
Added a new function start that can initialize the game (and the buttons).
Added start and restart buttons at the beginning and when game ends.
symbols is no more popped, because otherwise you wouldn't have any more symbols for the second game.
When switching from starting the game to the actual game (and vice versa), all the widgets on the root are grid_forget so that you can delete any shown button and display instead some new buttons.
Your game is really nice. If you want some simple improvements from here, I'd suggest you to:
Have symbols with only unique unicodes, and then just doing symbols *= 2 so that you avoid bothering with whether you've duplicated all the symbols or not.
Instanciate your game as a class. In this way, you avoid using global variables which may mess up you program.
Fix a size of your window so that it does not shrink and enlarge when switching game.
Determine the width and height of your grid automatically, to avoid defining ranges "4" and "6".
Related
I am trying to write a tic-tac-toe in Python but I get this error: 'Event' object is not subscriptable
The general idea is that when a button is pressed, if it doesn't have a text, it will change it's text in an "X" or "O" depending on the turn.
But I am stuck due to this error.
I have not much experience with Python so I need a fix easy to understand and implement. Thanks!
Code:
from tkinter import *
window = Tk()
btn = [None] * 9
btn_id = [None] * 9
#position of button array
def BTNposition():
i = 0
c = 0
k = 0
while i < 9:
#declaration of button array
btn[i] = Button(window, text = "", height = 10, width = 20, borderwidth = 5, command = lambda i=i: GAME)
btn[i].place(x= c, y= k)
btn_id[i] = btn[i]
print(btn[i], btn_id[i])
i+= 1
if c == 300:
k += 150
c = 0
else:
c += 150
BTNposition()
def GAME(btn):
turn = 0
if btn["text"] == "" and turn % 2 == 0: #here the error
btn.config(text = "X")
turn += 1
elif btn["text"] == "" and turn % 2 == 1:
btn.config(text = "O")
turn += 1
i = 0
#while i < 9:
#btn[i].bind("<Button 1>", GAME)
#find button pressed
window.bind("<Button 1>", GAME)
#print(click_position.btnp_x, click_position.btnp_y)
window.title('PyTris')
window.geometry("700x450")
window.mainloop()
I will update the post whenever I do major changes to the code.
Several problems:
window.bind("<Button 1>", GAME) a useless button that calls to GAME without providing a parameter hence it getting provided the default ButtonPressEvent as parameter which GAME is indexing into wich leads to the reported error
no exact button placement although you calculate positions
confused usages of = vs == inside GAME function
use of global variables with danger of confusing global and local identically named ones
variable names reused (or not used at all in case of the lambda i)
Fixing your code with minimalistic changes to it:
from tkinter import *
window = Tk()
btn = [None] * 9
turn = 0
#position of button array
def BTNposition():
c, k = 0, 0
for i in range(9):
# put button in array, callback is provided with the buttons index
# in the global array holding all the buttons
btn[i] = Button(window, text="", height=10, width=20, borderwidth=5,
command= lambda idx=i: GAME(idx))
# fix button on coordinates
btn[i].place(x=c, y=k)
if c == 300:
k += 150
c = 0
else:
c += 150
# btn_nr is supposed to be an index into the global btn-array holding your buttons
def GAME(btn_nr):
global turn # turn count is held globally, we want to modify it
button = btn[btn_nr] # get the button we are at
# only allow modification on buttons that are not yet X or O
if button["text"] == "":
# place the correct marker and advance turns
button["text"] = "X" if turn % 2 == 0 else "O"
turn += 1
BTNposition()
window.title('PyTris')
window.geometry("700x450")
window.mainloop()
These are few lines from my actual code - I am aware this is not the best way of writing a code, but as I am new and getting familiarize with Tkinter (py2) consider this as my scratch work.
I am listing a question and multiple options. When the user selects an option, a SUBMIT button is created and when clicks on SUBMIT button it will accordingly change the color of Option to green or red. If green then another NEXT button will be available to clean and move to next question.
The issue that I am facing is if a user selects option A but then without clicking the SUBMIT button selects another option the submit button multiplies. I want to destroy the unwanted buttons or even do not want to create multiple SUBMIT buttons.
Please do help in achieving the same.
import Tkinter
from Tkinter import *
import yaml
import random
grey = "#808080"
offwhite = "#e3e3e3"
filepath = "chapter-2.yaml"
tk = Tkinter.Tk()
tk.title("iCodet Learnings")
tk.geometry("800x600")
x = ''
tk.config(background=offwhite)
tk.resizable(0,0)
q_count = 0
def yaml_loader(filepath):
with open (filepath, "r") as fileread:
data = yaml.load(fileread)
return data
def cleaner(hint):
global rbutton
global q_count
global quest_label
global radio1
global button_game
quest_label.destroy()
radio1.destroy()
# destroys the radio buttons
for b in rbutton:
b.destroy()
# destroys the SUBMIT button
button_game.destroy()
# go to ext question
if hint == 'next':
q_count += 1
game_loop()
# This is display the first element from the yaml i.e the question
def display_question(questions, qc):
global quest_label
q = questions.keys()[qc]
a = questions[q]
v = a.keys()
quest_label = Label(tk, text = q, font = ("Consolas", 16), width = 500, justify = "center", wraplength = 400)
quest_label.pack(pady = (50,0))
return v
# This is for selecting the radio buttons
def selected():
global radio_default, button_next,radio1, val
global x, data,q_count, vali, rbutton, select_val
x = radio_default.get()
select_val = rbutton[x]
if q_count <= len(data):
q = data.keys()[q_count]
a = data[q] #second dictionary
v = a.keys() #second dictionary keys
# True or False from Yaml
val = a[v[x]][0]
press_button(val)
else:
print ("Mid way")
# This will list all the options under question
def display_answer(ans):
global radio1, rbutton
global x, q_count
global radio_default
radio_default = IntVar()
rbutton = []
rad_select = []
val_count = 0
for i in ans:
radio1 = Radiobutton(tk, text = i, font = ("times", 14, "bold"), value = val_count, variable = radio_default, command = selected, background = 'NavajoWhite3')
rbutton.append(radio1)
val_count += 1
radio1.pack(pady = (30,0))
radio_default.set(-1)
# This displays the SUBMIT buuton
def press_button(val):
global button_game
# true
if val:
button_game = Button(tk, text = 'SUBMIT', font = ("default", 15, "bold"), bg='orange', fg = 'white', border=2, height = 2, width = 8, command = lambda: cleaner('next'))
button_game.pack(pady = (30,0))
# false
elif not val:
print "Do nothing"
button_game = Button(tk, text = 'SUBMIT', font = ("default", 15, "bold"), bg='orange', fg = 'white', border=2, height = 2, width = 8, command = lambda: cleaner('stay'))
button_game.pack(pady = (30,0))
return True
def game_loop():
global q_count
global x, data
global quest_label, button_game
action = True
data = yaml_loader(filepath)
if q_count <= len(data)-1:
l_ans = display_question(data, q_count)
display_answer(l_ans)
else:
txt_label = Label(tk, text = "CONGRATULATIONS ON COMPLETING CHAPTER", font = ("Comicsans", 24, "bold"), background = offwhite, wraplength = 700)
txt_label.pack(pady = (100,0))
button_end = Button(tk, text = 'THANK YOU !', font = ("default", 15, "bold"), bg='saddle brown', fg = 'white', border=2, height = 3, width = 10, command = tk.destroy)
button_end.pack(pady = (50,0))
game_loop()
tk.mainloop()
chapter-1.yaml
> "What’s the complete name of Sachin Tendulkar ?":
> "Sachin Ramya Tendulkar":
> - False
> "Sachin Ramesh Tendulkar":
> - True
> "Sachin Tendehar":
> - False
> " Sachin 10dulkar":
> - False
> "Hint":
> - "biscuit & cookies"
As things are, each time press_button() is run, a new Button object is generated, and placed in the button_game variable. This does not remove or hide the previous button, which still exists in the packed UI.
A simple solution that would save the machine some work is to initialize the button only once, earlier in the code, but omit placing/displaying/packing it until that block within press_button() is run.
I was able to achieve what I was looking for with the help of config.
I created the SUBMIT button once at the beginning and then instead of calling the whole function again and again; I just replaced press_button with button_game.config(command = lambda: right(chapter, num_ques, topic, val))
Now I should write this code using class in python.
Im trying to stop well-working drawing function through a stop button. After pressing the start button I can insert rectangles. That's what I want. However, if I activate the stop button (see code below) which set the condition to insert = false, stil can insert rectangles. This should not be the case. What's going wrong?
import tkinter as tk
def insert_cells():
global insert
def xyposition(event):
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
c = cell-1
x0 = int((x//cell)*cell+1)
y0 = int((y//cell)*cell+1)
canvas.create_rectangle(x0,y0,x0+c,y0+c, width=0, fill='green')
if not(insert):
return
else:
canvas.bind("<Button-1>", xyposition)
def stop_insert_cells():
global insert
insert = False
# MAIN
root = tk.Tk()
root.geometry("400x400")
n=30
m=30
cell=10
w = n*cell
h = m*cell
# canvas
canvas = tk.Canvas(root,width = w, height = h, borderwidth = 0, highlightthickness=0)
canvas.place(x=20, y=20)
# border canvas
canvas.create_rectangle(0,0,w-1,h-1, outline = 'black')
# raster canvas
x1 = 1
y1 = 1
x2 = w-2
y2 = h-2
for i in range(1,m): # horizontal lines
canvas.create_line(x1,i*cell,x2,i*cell)
for i in range(1,n): # vertical lines
canvas.create_line(i*cell,y1,i*cell,y2)
# cell filling
insert = True
start_insert_button = tk.Button(root, text='start', command = insert_cells)
stop_insert_button = tk.Button(root, text = 'stop', command = stop_insert_cells)
stop_insert_button.pack(side='bottom')
start_insert_button.pack(side='bottom')
root.mainloop()
Since you have binded the <Button-1> event to a callback when the start button is clicked, but do not unbind the callback when the stop button is clicked. So the binded callback will still be called when the canvas is clicked.
Either you unbind the callback (in this case, do not need the global variable insert):
def insert_cells():
def xyposition(event):
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
c = cell-1
x0 = int((x//cell)*cell+1)
y0 = int((y//cell)*cell+1)
canvas.create_rectangle(x0,y0,x0+c,y0+c, width=0, fill='green')
canvas.bind("<Button-1>", xyposition)
def stop_insert_cells():
canvas.unbind('<Button-1>')
or bind the callback at program start, then setting global variable insert to start or stop the insertion:
def xyposition(event):
if insert:
x = canvas.canvasx(event.x)
y = canvas.canvasy(event.y)
c = cell-1
x0 = int((x//cell)*cell+1)
y0 = int((y//cell)*cell+1)
canvas.create_rectangle(x0,y0,x0+c,y0+c, width=0, fill='green')
def insert_cells():
global insert
insert = True
def stop_insert_cells():
global insert
insert = False
...
# cell filling
insert = False
canvas.bind('<Button-1>', xypostion)
Note that your code may create multiple rectangles at the same cell. Better do some housekeeping to check whether the rectangle at the cell is already created.
I am following a beginner's Python guide book and I am stuck with one particular task with tkinter. I've followed the code from the book but it still does not seem to work properly:
The purpose is to make a guessing game where buttons are pressed two times. If the user finds two same symbols after guessing two times in a row, those buttons/symbols are disabled and stay visible. In other case, they are hidden and guessing starts again. Problem is: After pressing the buttons they all stay visible. Please see attachment. Screenshot of outcome
For doing this code I am using Jupyter Notebook 5.5.0 which has worked well with other exercises in the book. I was wondering if it is about the notebook (also the graphics look different here than in the book) I am using or just a bug in the code?
Thanks in advance!
import random
import time
from tkinter import Tk, Button, DISABLED
def show_symbol(x, y):
global first
global previousX, previousY
buttons[x, y]["text"] = button_symbols[x, y]
buttons[x, y].update_idletasks()
if first:
previousX = x
previousY = y
first = False
elif previousX != x or previousY != y:
if buttons[previousX, previousY]["text"] != buttons[previousX, previousY]["text"]:
time.sleep(0.5)
buttons[previousX, previousY]["text"] = ""
buttons[x, y]["text"] = ""
else:
buttons[previousX, previousY]["command"] = DISABLED
buttons[x, y]["command"] = DISABLED
first = True
root = Tk()
root.title("Find a pair")
root.geometry("500x500")
root.resizable(width=False, height=False)
buttons = {}
first = True
previousX = 0
previousY = 0
button_symbols = {}
symbols = [u"\u2702", u"\u2702", u"\u2705", u"\u2705", u"\u2708", u"\u2708", u"\u2709", u"\u2709", u"\u270A", u"\u270A",
u"\u270B", u"\u270B", u"\u270C", u"\u270C", u"\u270F", u"\u270F", u"\u2712", u"\u2712", u"\u2714", u"\u2714",
u"\u2716", u"\u2716", u"\u2728", u"\u2728"]
random.shuffle(symbols)
for x in range(6):
for y in range(4):
button = Button(command=lambda x=x, y=y: show_symbol(x, y), width=3, height=3)
button.grid(column=x, row=y)
buttons[x, y] = button
button_symbols[x, y] = symbols.pop()
root.mainloop()
The following if statement will always return false because it is checking if itself is not equal to itself. A simple oversight ;)
if buttons[previousX, previousY]["text"] != buttons[previousX, previousY]["text"]:
Just change it to the following:
if buttons[previousX, previousY]["text"] != buttons[x, y]["text"]:
I tested your code and it works with that change
I was wondering if there is an easy way to create a grid of checkboxes using Tkinter. I am trying to make a grid of 10 rows and columns (so 100 checkboxes) so that only two checkboxes can be selected per row.
Edit: I'm using python 2.7 with spyder
What I have so far:
from Tkinter import*
master = Tk()
master.title("Select Groups")
rows=10
columns=10
for x in range(rows):
for y in range(columns):
Label(master, text= "Group %s"%(y+1)).grid(row=0,column=y+1)
Label(master, text= "Test %s"%(x+1)).grid(row=x+1,column=0)
Checkbutton(master).grid(row=x+1, column=y+1)
mainloop()
I'm trying to use state='Disabled' to grey out a row once two checkboxes have been selected.
Here's a version that puts everything into a class so we don't need to use global variables. It also avoids the import * construction which is generally considered bad style in Python. True, lots of example code uses import * but it's not a good practice because it clutters up the global namespace with all the names from the imported module. So those names can clash with the names of your own variables, and they can also clash with the names of other modules you import using import *.
The program prints lists of the selected Groups for each Test row when the window closes.
#!/usr/bin/env python
''' Create a grid of Tkinter Checkbuttons
Each row permits a maximum of two selected buttons
From http://stackoverflow.com/q/31410640/4014959
Written by PM 2Ring 2015.07.15
'''
import Tkinter as tk
class CheckGrid(object):
''' A grid of Checkbuttons '''
def __init__(self, rows=10, columns=10):
master = tk.Tk()
master.title("Select Groups")
rowrange = range(rows)
colrange = range(columns)
#Create the grid labels
for x in colrange:
w = tk.Label(master, text="Group %s" % (x + 1))
w.grid(row=0, column=x+1)
for y in rowrange:
w = tk.Label(master, text="Test %s" % (y + 1))
w.grid(row=y+1, column=0)
#Create the Checkbuttons & save them for future reference
self.grid = []
for y in rowrange:
row = []
for x in colrange:
b = tk.Checkbutton(master)
#Store the button's position and value as attributes
b.pos = (y, x)
b.var = tk.IntVar()
#Create a callback bound to this button
func = lambda w=b: self.check_cb(w)
b.config(variable=b.var, command=func)
b.grid(row=y+1, column=x+1)
row.append(b)
self.grid.append(row)
#Track the number of on buttons in each row
self.rowstate = rows * [0]
master.mainloop()
def check_cb(self, button):
''' Checkbutton callback '''
state = button.var.get()
y, x = button.pos
#Get the row containing this button
row = self.grid[y]
if state == 1:
self.rowstate[y] += 1
if self.rowstate[y] == 2:
#Disable all currently off buttons in this row
for b in row:
if b.var.get() == 0:
b.config(state=tk.DISABLED)
else:
self.rowstate[y] -= 1
if self.rowstate[y] == 1:
#Enable all currently off buttons in this row
for b in row:
if b.var.get() == 0:
b.config(state=tk.NORMAL)
#print y, x, state, self.rowstate[y]
def get_checked(self):
''' Make a list of the selected Groups in each row'''
data = []
for row in self.grid:
data.append([x + 1 for x, b in enumerate(row) if b.var.get()])
return data
def main():
g = CheckGrid(rows=10, columns=10)
#Print selected Groups in each Test row when the window closes
data = g.get_checked()
for y, row in enumerate(data):
print "Test %2d: %s" % (y + 1, row)
if __name__ == '__main__':
main()
Here's an example using your provided 10x10 grid. It should give you the basic idea of how to implement this.
Just make sure you keep a reference to every Checkbutton (boxes in the example) as well as every IntVar (boxVars in the example).
Here's why:
-Checkbuttons are needed to call config(state = DISABLED/NORMAL).
-IntVars are needed to determine the value of each Checkbutton.
Aside from those crucial elements its basically just some 2D array processing.
Here's my example code (now based off of your provided code).
from Tkinter import *
master = Tk()
master.title("Select Groups")
rows=10
columns=10
boxes = []
boxVars = []
# Create all IntVars, set to 0
for i in range(rows):
boxVars.append([])
for j in range(columns):
boxVars[i].append(IntVar())
boxVars[i][j].set(0)
def checkRow(i):
global boxVars, boxes
row = boxVars[i]
deselected = []
# Loop through row that was changed, check which items were not selected
# (so that we know which indeces to disable in the event that 2 have been selected)
for j in range(len(row)):
if row[j].get() == 0:
deselected.append(j)
# Check if enough buttons have been selected. If so, disable the deselected indeces,
# Otherwise set all of them to active (in case we have previously disabled them).
if len(deselected) == (len(row) - 2):
for j in deselected:
boxes[i][j].config(state = DISABLED)
else:
for item in boxes[i]:
item.config(state = NORMAL)
def getSelected():
selected = {}
for i in range(len(boxVars)):
temp = []
for j in range(len(boxVars[i])):
if boxVars[i][j].get() == 1:
temp.append(j + 1)
if len(temp) > 1:
selected[i + 1] = temp
print selected
for x in range(rows):
boxes.append([])
for y in range(columns):
Label(master, text= "Group %s"%(y+1)).grid(row=0,column=y+1)
Label(master, text= "Test %s"%(x+1)).grid(row=x+1,column=0)
boxes[x].append(Checkbutton(master, variable = boxVars[x][y], command = lambda x = x: checkRow(x)))
boxes[x][y].grid(row=x+1, column=y+1)
b = Button(master, text = "Get", command = getSelected, width = 10)
b.grid(row = 12, column = 11)
mainloop()