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

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

Related

How to add commands only usable after using a certain command in tkinter?

I am trying to make some sort of command box with tkinter text boxes here is an example
import tkinter as tk
screen = tk.Tk()
screen.geometry('900x700')
texte = tk.Text(screen, height="5")
texte.place(x = 100, y = 0)
texte.pack()
label = tk.Label(screen)
label.pack()
def on_button(event=None):
inp = texte.get(1.0, "end-1c")
if inp in ("/example", "/Example"):
# change text in existing label
label['text'] = "Loading Tournament".format(inp)
else:
label['text'] = "{0} is not a valid command".format(inp)
btn = tk.Button(screen, text="Enter", command=on_button)
btn.pack()
#texte.bind('<Return>', on_button)
texte.focus()
screen.mainloop()
when I type /example it prints the a command. I want to make it so I can type another command after and only after I type /example but using the same text box.
Your question is somewhat vague about what you want to happen each time a command it typed in, so that part of this is just a guess. As for the main topic of changing what allowable after some other command, I suggest you define a variable to keep track of what processing has occurred which I've named state in the code below. This way of processing input is called using a state machine.
The state variable is an integer and is initialized to 0. As first valid command is entered by the the user, the state variable is assigned the value 1 to indicate that has happened and begin allowing other commands to be entered. In that state the code below will except just about anything at that point although it does check for a special "restart" command to reset the state back to 0 and start things over. You could, of course expand, on this theme and have many more states with associated command as desired.
import tkinter as tk
screen = tk.Tk()
screen.geometry('900x700')
texte = tk.Text(screen, height="5")
texte.place(x=100, y=0)
texte.pack()
label = tk.Label(screen)
label.pack()
state = 0
def on_button(event=None):
global state
inp = texte.get(1.0, 'end-1c')
if state == 0:
if inp.lower() == "/example":
label['text'] = f"Loading Tournament {inp}"
state = 1
else:
label['text'] = f"{inp} is not a valid command"
# Stay in this state...
elif state == 1:
if inp.lower() == "restart":
label['text'] = "restarted"
state = 0
else:
label['text'] = f"processing command {inp}"
# Stay in this state...
texte.delete('1.0', 'end') # Clear.
texte.mark_set("insert", '1.0') # Move insertion cursor to beginning.
return 'break' # Prevent further processing of event.
btn = tk.Button(screen, text="Enter", command=on_button)
btn.pack()
texte.bind('<Return>', on_button)
texte.focus()

Python how to use Tkinter GUI without interfering the main code loop

I would like to implement a very simple GUI for my project. I was previously using just Print statements to output some text and data. However, that is not very conveneint and since a person will need to operate a device that I am coding, he needs to be clearly see the instructions that I am going to display on GUI.
my code:
main()
myConnection = mysql.connector.connect( host=hostname, user=username, passwd=password, db=database )
counter = 0
window = tk.Tk()
window.title("GUI")
window.geometry("400x200")
while(1):
# OPERACIJOS KODAI:
# 0 - PILDYMAS
# 1 - KOMPLEKTAVIMAS
# 2 - NETINKAMAS KODAS
tk.Label(window,text = "Scan barcode here:").pack()
entry = tk.Entry(window)
entry.pack()
var = tk.IntVar()
button = tk.Button(window,text="Continue",command = lambda: var.set(1))
button.pack()
print("waiting...")
button.wait_variable(var)
result = entry.get()
print("Entry string=",result)
var.set(0)
operacijos_kodas=Scanning_operation(myConnection,result)
print("operacijos kodas=",operacijos_kodas)
if(operacijos_kodas == 0):
tk.label(window,text = "PILDYMO OPERACIJA:").pack()
pildymo_operacija(myConnection)
elif(operacijos_kodas == 1):
tk.Label(window,text = "PAKAVIMO OPERACIJA:").pack()
insertData_komplektacija(myConnection,"fmb110bbv801.csv");
update_current_operation(myConnection);
picking_operation();
elif(operacijos_kodas == 2):
print("Skenuokite dar karta")
#break
window.mainloop();
Nothing is being displayed. It just opens up an empty GUI window.
First of all, I am unsure where should I call function window.mainloop().
Secondly, since my system runs in an infinite while loop ( the operation starts when a user scans a bar-code, then he completes an operation and the while loop starts over again (waiting for user to scan a bar-code). So I just simply have to display some text and allow user to input data in the text box.
Could someone suggest me whether this GUI is suitable for my needs or I should look for an alternatives?
UPDATE*********************
I have tried to use mainloop:
print ("Using mysql.connector…")
main()
GPIO_SETUP()
myConnection = mysql.connector.connect( host=hostname, user=username, passwd=password, db=database )
counter = 0
window = tk.Tk()
window.resizable(False,False)
window_height = 1000
window_width = 1200
#window.attributes('-fullscreen',True)
#window.config(height=500,width=500)
#can = Canvas(window,bg='red',height=100,width=100)
#can.place(relx=0.5,rely=0.5,anchor='center')
window.title("GUI")
screen_width = window.winfo_screenwidth()
screen_height= window.winfo_screenheight()
x = int((screen_width/ 2) - (window_width / 2))
y = int((screen_height/ 2) - (window_height / 2))
window.geometry("{}x{}+{}+{}".format(window_width,window_height,x,y))
label1=Label(window,text = "SKENUOKITE BARKODA(GUID) ARBA DAIKTO RIVILINI KODA:")
label1.pack()
entry = Entry(window)
entry.pack()
var = tk.IntVar()
button = Button(window,text="Testi operacija",width = 30,command = lambda: var.set(1))
button.pack()
#button2 = Button(window,text="RESTARTUOTI SISTEMA",width = 30,command = restart_devices())
#button2.pack()
print("waiting...")
button.wait_variable(var)
Scanned_serial = entry.get()
print("Entry string=",Scanned_serial)
var.set(0)
label2=Label(window,text = "Vykdoma operacija:")
label2.pack()
window.update()
window.after(1000,Full_operation(Scanned_serial,label2,window))
window.mainloop()
This is my code. As you can see. i call Full_operation function and then window.mainloop()
my Full_operation:
def Full_operation(Scanned_serial,label2,window):
operacijos_kodas=Scanning_operation(myConnection,Scanned_serial)
print("operacijos kodas=",operacijos_kodas)
if(operacijos_kodas == 0):
label2.config(text = "SPAUSKITE MYGTUKA ANT DEZES KURIA NORITE PILDYTI:")#update the label2
window.update()#call update to update the label
pildymo_operacija(myConnection,Scanned_serial,label2,window)
elif(operacijos_kodas == 1):
insertData_komplektacija(myConnection,"fmb110bbv801.csv");
update_current_operation(myConnection);
#label2.config(text = "IMKITE DAIKTUS IS ZALIOS DEZUTES:")#update the label2
picking_operation(myConnection,label2);
elif(operacijos_kodas == 2):
print("Skenuokite dar karta")
label2.config(text = "NUSKENUOTAS NEGALIMAS KODAS:")#update the label2
window.update()#call update to update the label
How can I ensure that everytime I enter FUll_operation function I start from clean GUI again and start another operation.
Now I am able to complete operation once. After that, the GUI is not responsive.
I have added a print statement at the beggining of my full_operation and it does not execute after I complete it once so my mainwindow does not seem to work properly.
You'll need to adapt your code to work with a GUI. You can't introduce infinite loops in to tkinter GUI's without causing all sorts of problems.
Mainloop should only be called once.
I'd suggest that you move all of your scanning/saving operations in to a separate function which you schedule to occur periodically using the tkinter after method.
For example if you call your function scan you would schedule it to occur after 1 second using
root.after(1000, scan)
A more advanced method would be to have your scanning code running on a separate thread.
Also, you are currently trying to create the label each time you go round the while loop rather than just creating and packing them once and updating the text of the labels when you perform the "scanning". You can update the text of a label using the config method, for example
## Create a label
label1 = tk.Label(window,text = "PAKAVIMO OPERACIJA:")
##Pack the label
label1.pack()
## Update the text later
label1.config(text="New Text")
Here is an example of updating tkinter widgets periodically from a function.
import tkinter as tk
import random
def scanning():
num = random.randint(0,100)
entryTemperature.delete(0, tk.END) #Delete the current contents
entryTemperature.insert(0, f"{num} K") #Add new text
root.after(1000, scanning) #Schedule the function to run again in 1000ms (1 second)
root = tk.Tk()
entryTemperature = tk.Entry(root)
entryTemperature.grid(padx=50,pady=50)
root.after(1000, scanning)
root.mainloop()

tkinter window freezes after some time

I am new to Tkinter. I was experimenting with this, but while the Tkinter starts just fine, it some times freezes after a while for no reason. Actually, the weird thing that happens, is that while the Tkinter windows do not update the information anymore, the counter keeps printing ON MY WINDOWS DESKTOP LEFT TOP SIDE! (just the black bg box with the white font that shows the counter number) and eventually finds number 777 and ends. But long freezes in between, even without any printing (not on my Windows desktop either)
this is the normal tkinter window
this is the console output
all good here
crazy Windows display here
The simple program here is generating random ints from 1 - 10000 and should 1) if 777 is found it should print the counter and exit 2) if any number between 1-100 is found, it should print the number and counter.
I am trying to learn how to print new content in the same line, and if something happens print more info on another line, while the first (or more) keep updating.
I'm not sure if I exit the program the right way or how exactly I should be using/calling the Tkinter function to update the info on the screen.
Any help or tips most appreciated.
from tkinter import *
import random
import time
tk=Tk()
i = 0
counter = 0
tk.title("Testing...")
tk.geometry('300x200')
canvas_width = 200
canvas_height = 100
canvas = Canvas(tk, width=canvas_width, height=canvas_height, bg='white')
canvas.pack(expand=YES)
def screen(Found):
#canvas.create_text(20, 20, text="Counter:" + str(counter), font="Times 20 italic")
widget = Label(canvas, text="Counter:" + str(counter), fg='white', bg='black')
widget.grid(column=0, row=0)
if Found:
widget = Label(canvas, text="Number " + str(i) +" was found at counter:" + str(counter), fg='white', bg='black')
widget.grid(column=0, row=1)
found = False
loop = True
while loop:
counter += 1
i = random.randint(1, 10000)
if i in range(1, 100):
print("Found i = ", i, " in counter:", counter)
Found = True
else:
Found = False
if i == 777:
loop = False
print("JUST FOUND 777")
print("Counter:", counter)
screen(Found)
tk.update_idletasks()
tk.update()
# tk.mainloop()
As a matter of design, it is usually a better approach to rely on the tkinter mainloop to drive the application. root.after is a great substitute to a while loop. Labels do not need to be recreated; they can use special variables that update the displayed values themselves, and their properties can be reconfigured. Calling update, and update_idletasks is hardly ever needed.
The following is maybe a better approach where one single label is updated from a loop powered by the tkinter mainloop:
(it shows you "how to keep displaying new stuff, without such label issues")
import tkinter as tk
import random
def screen(found, v, c):
if found:
label.configure(bg='red', fg='blue')
print('found')
else:
label.configure(bg='black', fg='white')
update_lbl_txt(v, c)
def update_lbl_txt(v=0, c=0):
txt = f'Number at {v} was found at counter {c}'
lbl_txt.set(txt)
def found_target(t):
return t in range(1, 100) or t == 777
def loop(c=0):
v = random.randint(1, 10000)
screen(found_target(v), v, c)
root.after(100, loop, c+1)
root = tk.Tk()
root.title("Testing...")
root.geometry('300x200')
lbl_txt = tk.StringVar(root)
update_lbl_txt()
label = tk.Label(root, textvariable=lbl_txt, fg='white', bg='black')
label.pack(expand=True, fill=tk.BOTH)
loop()
root.mainloop()
The reason it seems to freeze it at least partly because you have an infinite loop that you never, ever break out of. Most of the time i will not be in the range of 1 to 100, nor will it be 777, but your loop will keep on running and never update the screen.
You're also creating hundreds or thousands of label widgets every second, stacked on top of one another. You will run into performance issues rather quickly.

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

Changing button text with button click not working

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 = []

Categories