Changing button text with button click not working - python

I am programming Minesweeper. I have completed all of the logic and now I am just doing the GUI. I am using Tkinter. With so many spaces on the board, I want to automate the creating of all these buttons, which I have done this way:
button_list = []
def create_buttons():
# Create the buttons
for x in range(code_squares):
# Code_squares is how many squares are on the board
new_button = Button(frame_list[x], text = "", relief = RAISED)
new_button.pack(fill=BOTH, expand=1)
new_button.bind("<Button-1>", lambda event: box_open(event, x))
button_list.append(new_button)
def box_open(event, x):
if box_list[x] == "M":
# Checks if the block is a mine
button_list[x].config(text="M", relief = SUNKEN)
# Stops if it was a mine
root.quit()
else:
# If not a mine, it changes the button text to the xth term in box_list, which is the amount of nearby mines.
print("in")
button_list[x].config(text=box_list[x], relief = SUNKEN)
The print statement is just a test. When I click a spot, it will execute the print statement, so I know its getting there, but it will not change the button text. All help is much appreciated!

I believe the issue is the way you're trying to embed x in your lambda, give this a try instead to see if it solves your problem:
from functools import partial
def create_buttons():
for n in range(code_squares):
# Code_squares is how many squares are on the board
new_button = Button(frame_list[n], text="", relief=RAISED)
new_button.pack(fill=BOTH, expand=1)
new_button.bind("<Button-1>", partial(box_open, x=n))
button_list.append(new_button)
def box_open(event, x):
if box_list[x] == "M":
# Checks if the block is a mine
button_list[x].config(text="M", relief=SUNKEN)
# Stops if it was a mine
root.quit()
else:
# If not a mine, it changes the button text to the xth
# term in box_list, which is the amount of nearby mines.
button_list[x].config(text=box_list[x], relief=SUNKEN)
button_list = []

Related

Python: why does first restart works, but second - doesn't?

This is a simple minesweeper game with implementation of windows user interface
The only thing this function has to do is to erase all information (such as text) on the buttons, as well as to create a new random array. It works completely well the 1st time after i press the button, but the second it doesn't work. 2nd time it erases everything (it does its job again as planned), but other functions don't work (I press the buttons after the 2nd restart, and nothing happens, but after the 1st restart everything is fine).
What's going on?? Is it a problem of the memory, where variables are stored, or a specific of the graphical user interface, I am not aware of?
from tkinter import *
def new_game():
lost = False
label['text'] = str(mines) + ' mines left'
global mine_sweep
mine_sweep = mine_randomization().tolist()
for row in range(10):
for col in range(10):
buttons[row][col]['text'] = ''
window = Tk()
window.title('minesweeper')
label = Label(text=str(mines)+' mines left', font=('consolas', 20))
label.pack(side='top')
reset_button = Button(text='restart', command=new_game)
reset_button.pack(side='top')
buttons = buttons.tolist()
frame = Frame(window)
frame.pack()
for row in range(10):
for col in range(10):
buttons[row][col] = Button(frame, text='', font=('consolas', 10),
width=2, height=1,
command= lambda row=row, col=col: cell(row, col))
buttons[row][col].grid(row=row, column=col)
window.mainloop()
(I can't place the whole program here, just part which doesn't work)
here is what the function cell does:
def cell(row, col):
global lost
if buttons[row][col]['text'] == '' and mine_sweep[row][col] == 0 and not lost:
open_fields(row, col)
elif buttons[row][col]['text'] == '' and mine_sweep[row][col] == 1 and not lost:
buttons[row][col].config(bg='red', font=('consolas', 10))
buttons[row][col]['text'] = '*'
label['text'] = 'You lost!'
lost = True
if check_win():
label['text'] = 'You win!'
Yes, #Matiiss was wright, and the solution was that the variable lost is used in different functions, that's why it should be global. Moreover, when the first game is complete, the lost should again be set to false, in order to start a new game and the computer to know you have actually not yet 'lost'.

How to add a reset button that clears my code?

I'm making a basic TicTacToe UI in python, and I believe that a fundamental item to this code is a reset button which resets your codes back to the default. is there any other way to do this?
I've Tried to define a function which resets the text of the button back to " " but I don't think that's a great idea because of a lot of other complexities within the cod.
from tkinter import *
root = Tk()
def changetext():
BTN1["text"] = "X"
BTN1 = Button(root, text=" ", command=changetext)
BTN1.pack()
root.mainloop()
So I want to add a button here that says "Reset Text" and it resets all the codes to defaults.
The easiest way to reset the game would be to
Reset the UI as you suggest, with a single dedicated reset_UI() function
Reset the board state by creating a new game board object, and discarding the old one
This of course means that you'll need to wrap all your variables and functions in a board class Board, so that there aren't a billion global variables you have to worry about resetting. The only thing that should persist between resets are your UI buttons, which can be created in your main() function before initializing the game board.
Here's code demonstrating how something like that could be done (plus a few other things):
import tkinter as tk
def toggle(btn):
if btn["text"] == "X":
btn["text"] = " "
else:
btn["text"] = "X"
def reset(buttons):
for btn in buttons.values():
btn["text"] = " "
root = tk.Tk()
buttons = {}
for row in range(3):
for col in range(3):
button = tk.Button(root, text=" ", width=1, height=1)
button.config(command=lambda btn=button: toggle(btn))
button.grid(row=row, column=col)
buttons[row, col] = button
reset_button = tk.Button(root, text="Reset", command=lambda: reset(buttons))
reset_button.grid(columnspan=3)
root.mainloop()

Problem with calling functions with tkinter

from tkinter import *
from random import *
root = Tk()
#A function to create the turn for the current player. The current player isnt in this code as it is not important
def turn():
window = Tk()
dice = Button(window, text="Roll the dice!", bg= "white", command=lambda:diceAction(window))
dice.pack()
window.mainloop()
#a function to simulate a dice. It kills the function turn.
def diceAction(window):
result = Tk()
y = randint(1, 6)
quitButton = Button(result, text="Ok!", bg="white", command=result.destroy)
quitButton.pack()
window.destroy()
result.mainloop()
#A function to create the playing field and to start the game
def main():
label1 = Button(root, text="hi", bg="black")
label1.pack()
while 1:
turn()
print("Hi")
turn()
main()
root.mainloop()
My problem is that the code in the while function after the first turn() the code isnt executed until i close the root window(which i dont want because it represents the playing field). You can copy this code and execute it yourself if you want.
I have no idea what causes this and havent found anything online. Sorry for the long code but i wrote it so that it is executeable.
I don't know why this particular problem is occurring, but there are a couple of things in your code that are considered bad practice.
Instead of creating multiple instances of Tk(), you should use Toplevel widgets for any pop-up windows needed. Also, it's better to use root.mainloop() to run the program rather than a while loop.
I've made some edits to your code so that it uses a Toplevel widget and discards of the while loop.
from tkinter import *
from random import *
#A function to create the turn for the current player. The current player isnt in this code as it is not important
def turn(prev=None):
# destroy the previous turn
if prev:
prev.destroy()
# pop up with dice
window = Toplevel()
dice = Button(window, text="Roll the dice!", bg= "white")
dice.config(command=lambda b=dice, w=window:diceAction(b, w))
dice.pack()
#a function to simulate a dice, reconfigures the pop-up
def diceAction(button, window):
# roll dice
y = randint(1, 6)
# use dice result here?
print(y)
# reconfigure button, the command now starts a new turn
button.config(text='ok', command=lambda w=window:turn(prev=w))
root = Tk()
# I hijacked this button to use as a start button
label1 = Button(root, text="hi", bg="black", command=turn)
label1.pack()
root.mainloop()
I don't know if this is what you need, but it functions as the code in the question would if it worked.
Sorry I couldn't help with the cause of the error.

How to "activate" certain parts of a code

I want to "activate" parts of my code through buttons. I tried something like, when you press a button, the value of a variable is set to an other amount which activates another part of my code:
from tkinter import *
window = Tk()
window.title('Adventure')
c = Canvas(window, height=360, width=640, bg='black')
c.pack()
system = 1
def start():
c.delete(anfang1)
anfangbutton.destroy()
if system == 1:
anfang1 = c.create_text(320, 180, text='Adventure', fill='white', font=('Halvatica', 50))
anfangbutton = Button(window, text='Start', command=start)
anfangbutton.place(x=320, y=250) # I want that if you press the button, start is activated and the value of "system" goes to 2, so the next part begins
if system == 2:
anfang2 = c.create_text(320, 180, text='Adventure', fill='white', font=('Halvatica', 50))
I would appreciate if someone could help me with this or has another way of doing it
Here is the code, I will explain:
def nextStage():
#CODE#
def start():
c.delete(anfang1)
anfangbutton.destroy()
anfang2 = c.create_text(320, 180, text='Adventure', fill='white', font=('Halvatica', 50))
#then button or code to move onto next "stage"
def main():
global anfang1
global anfangbutton
anfang1 = c.create_text(320, 180, text='Adventure', fill='white', font=('Halvatica', 50))
anfangbutton = Button(window, text='Start', command=start)
anfangbutton.place(x=320, y=250) # I want that if you press the button, start is activated and the value of "system" goes to 2, so the next part begins
main()
Basically, what I have done to try and combat the issue is two things.
Rather than trying to use a system variable and If/Else statements, I have put it all into functions. For tkinter, you want to build functions from the first one at the bottom of the page. So the last "stage" or function will be at the top (this is just so it can be properly referenced by the button).
I then globalised the variables so that start() would be able to destroy the buttons.
Not the most effective but works?

Creating a circle that changes in size depending on score in Tkinker (Python)

I really don't know Tkinter very well, I'm not even sure this is possible to do. But basically I want a visual representation of the score the user gets in the game i've programmed. As it works currently, the user gets to choose between "study" and "party", and depending on how he answers the tamaguchi either increases in size or decreases. The idea is that the tamaguchi is represented in Tkinter by a circle that corresponds with the score the user gets. I was thinking maybe I can have this in the root.mainloop()? So each time it goes through the loop, it deletes the last circle, and creates a new one with the updated score. This is what i've written so far:
class Application(Frame):
def __init__(self,master):
super(Application,self).__init__(master)
self.grid()
self.create_widgets()
self.circle()
def circle(self):
circle1.destroy()
r = int(tamaguchin.size)
self.circle1 = circle(r^2*3.14)
self.circle1.grid()
def create_widgets(self):
Label(self,
text = "Välkommen till spelet!"
).grid(row = 0, column = 6, sticky = W)
self.btn1 = Button(self, text = "study", command = lambda:self.update_text('plugga'))
self.btn1.grid(row=1,column=0)
self.btn2 = Button(self, text = "party", command = lambda:self.update_text('festa'))
self.btn2.grid(row=2,column=0)
self.btn3 = Button(self, text = "exit", command = self.exit)
self.btn5.grid()
def update_text(self,value):
message = "Your choice was",value,"which brings your last 3 choices to:"
print(message)
lista.append(value)
lista.remove(lista[0])
print(lista[0],'-',lista[1],'-',lista[2])
if lista in PositivLista:
tamaguchin.increasesize()
elif lista in NegativLista:
tamaguchin.decreasesize()
elif lista in HalveringsLista:
tamaguchin.halfsize()
else:
tamaguchin.samesize()
return lista
def exit(self):
print('You have chosen to exit the game')
root.destroy()
root = Tk()
root.title("Tamaguchi-game")
root.geometry("500x500")
app = Application(root)
app.grid()
root.mainloop()
I don't know if there's some sort of in-built function that can help me with this, but I haven't been able to find anything yet on my own. Of course my idea could be (and likely is) not very good so if anyone has a better idea on how to approach it, i'm all open; I'm a pretty big noob =] Thanks a lot for any help, i'm really stuck with this!
There are two ways to handle this issue.
1) Update the circle size on every change of the variable tamaguchin.size
2) Update the circle using a timer. Tkinker uses the after function to do this.
I am going to provide an example of the second approach:
def circle(self):
circle1.destroy()
r = int(tamaguchin.size)
self.circle1 = circle(r^2*3.14)
self.circle1.grid()
self.root.after(1000, self.circle)
This will update the circle every second. You can change the 1000 number to the update frequency (in milliseconds) that you want.

Categories