Why tkinter-buttons do all disable? - python

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

Related

How Do I add reset button on Python

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".

How to make a Button()s state continuous?

I have a for loop initiating buttons in a grid, and I want the buttons state to togglable, instead of turning off once the mouse button is no longer held. I also need that to know which button called the buttons' function, because I'm initializing a number of them in the same for loop, meaning they all call the same function once activated, anyone know how to help me?
EDIT:
minimal working example:
import tkinter as gui
def createWindow():
window = gui.Tk()
window.title("GoL")
icon = gui.PhotoImage(file='Not showing ya\'ll my files.png')
window.iconphoto(True, icon)
window.config(background="black")
label = gui.Label(window,\
text="Generation:",\
bg="black",\
fg="white",\
font=("Consolas",20))
label.pack()
return window
def newBoard(x = 10,y = 10):
window = createWindow()
for i in range(0, y):
for j in range(1, x+1):
button = gui.Button(window,bg="black",height=1,width=2,command=changeState)
button.place(x=23*(j-1),y=23*(i+2))
window.mainloop()
what I want is the function changeState to change the
You can achieve this by keeping a list of buttons (buttons), then using a lambda function to pass the row/column number to the button command function, which then allows you to change the properties of the selected button. In this case it toggles between black and white background colours.
def changeState(i, j):
global buttons
b = buttons[i][j]
b.config(bg = "white") if b.cget("bg") != "white" else b.config(bg = "black")
def newBoard(x = 10,y = 10):
global buttons
window = createWindow()
buttons = []
for i in range(0, y):
row = []
for j in range(1, x+1):
button = gui.Button(window,bg="black",height=1,width=2,command=lambda i=i, j=j-1: changeState(i,j))
row.append(button)
button.place(x=23*(j-1),y=23*(i+2))
buttons.append(row)
window.mainloop()

'Event' object is not subscriptable

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()

I'm trying to make a simple line drawing program with tkinter but it won't work

I'm trying to make this really simple program, all it does is store the current x/y pos of the mouse on the canvas and then use them to draw a line when you click for the second time. I've already bound it and I'm not getting any errors, it seems like it's not even being activated. Any help is greatly appreciated
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
#For colored lines
presses = 0
def click(event):
if presses == 0:
initX = int(c.canvasx(event.x))
initY = int(c.canvasy(event.y))
presses == 1
elif presses == 1:
c.create_line(initX, initY,
int(c.canvasx(event.x)),
int(c.canvasy(event.y)))
presses == 0
c.bind("<Button-1>", click)
mainloop()
How does something like this work for you?
from tkinter import *
main = Tk()
c = Canvas(main, width=600, height=600)
c.pack()
line = []
def click(event):
global line
X = int(c.canvasx(event.x))
Y = int(c.canvasy(event.y))
line.append((X,Y))
if len(line) > 1:
startX,startY = line[-2]
c.create_line(startX, startY, X, Y)
c.bind("<Button-1>", click)
mainloop()
I've changed around your code a bit to store a list of the X,Y coordinates that have been clicked on. If more than 1 point on the screen has been clicked, it will draw a line between the current point clicked on and the last point clicked on.
Reason your code wasn't working was that initX and initY are forgotten in between calls on the the click function. Adding them to a list solves this.

Python message box showing without stopping script

I have an script that create numbers randomly, between 5 and 55, in a infinite loop. My goal is to show a message box when the number 55 is created. I have try easygui, tkinter, ctypes,... and I have been able to create the message box, but when this box appears the loop stops and it doesn't continue untill the user clicks de OK button. Is there a way to show a message box without stopping the script?
I have been looking for information in forums but I haven't found someone with this problem, so I hope someone can help me.
This is the part of the code with the loop:
def contador():
for x in range(1):
aleatorio = random.randint(1,11)*5
if aleatorio ==55:
estado = "Red"
ctypes.windll.user32.MessageBoxW(0, u"Error", u"Error", 0)
elif aleatorio >=30:
estado = "Red"
else:
estado = "Green"
global t
t = threading.Timer(15.0, contador)
t.start()
t = threading.Timer(15.0, contador)
t.start()
messageboxes in most cases (tkinter, gtk, winapi) are modal windows. You can create your own dialog or you can use threads.
import random
from Tkinter import Tk
def show_error(text):
top = Toplevel(root)
Label(top, text=text).pack()
Button(top, text="OK", command=top.destroy).pack(pady=5)
def contador():
for x in range(100):
aleatorio = random.randint(1,11)*5
if aleatorio == 55:
estado = "Red"
show_error('Some error occurred: %s' % x)
elif aleatorio >=30:
estado = "Red"
else:
estado = "Green"
contador()

Categories

Resources