Can I access Button information, which is not assigned to variable? - python

it's my first post here. I'm trying to create a 5x5 table with random numbers. The goal is that the user is supposed to click from the smallest to biggest number on the table, and once he clicked correct one it should be disabled. I don't want to attach each button to variable. I've created windows with random number, but now I'd like to create function which would check if the clicked number is the smallest, and if the answer is yes I have to change it's state to DISABLED. I been sitting on this for over 4 hours and I have no idea how to access clicked button information like 'text', and how can I disable it after user clicks on it.
Here's what I got so far, working on dat function.
from tkinter import *
import random
def click(z=None):
global o
Button(state=DISABLED)
o=Tk()
y=0
listrow=[4,3,2,1,0]
numbers=[]
spr=IntVar()
while len(numbers) < 25:
r = random.randint(0,999)
if r not in numbers:
numbers.append(r)
for i in range(1,26):
Button(o, text=str(numbers[i-1]), width=10).grid(row=listrow[i%5], column=y)
if i == 5:
y+=1
elif i == 10:
y+=1
elif i==15:
y+=1
elif i==20:
y+=1
else:
continue
o.bind_all('<Button-1>', click)
o.mainloop()

Consider integrating a class object with defined modules for building controls and the button click event. Specifically, on each button click check the minimum of the numbers list and disable corresponding button item by passing the index integer as parameter. See adjustment below.
Credit to #BrenBarn for the clever lambda solution lambda i=i in button command call:
from tkinter import *
import random
class NUMapp():
def __init__(self):
self.root = Tk()
self.buildControls()
self.root.mainloop()
def buildControls(self):
self.root.wm_title("Random Number Picker")
x = 0; y = 0
self.numbers = []; self.numbtn = []
while len(self.numbers) < 25:
r = random.randint(0,999)
if r not in self.numbers:
self.numbers.append(r)
for i in range(1,26):
self.numbtn.append(Button(self.root, text=str(self.numbers[i-1]), width=10,
command=lambda i=i:self.btnclick(i-1)))
self.numbtn[i-1].grid(row=x, column=y)
x+=1
if i % 5 == 0:
x = 0
y += 1
def btnclick(self, mynum):
currnum = int(self.numbtn[mynum].cget('text')) # CAPTURE BUTTON TEXT
if currnum == min(self.numbers):
self.numbtn[mynum].config(state="disabled") # DISABLE BUTTON
self.numbers.remove(currnum) # REMOVE FOR NEW MINIMUM
NUMapp()

Related

Python tkinter - Change the Label created by a loop, how can I do?

I read a lot for it and I have the sentiment that there is no solution for me.
Context: user has to enter 6-digit calculation number in the entries. The 9x entries are created by a loop. I wrote the condition to detect if the user did a mistake : number which contains less or more than 6 digit or / and contains a letter. They are working, I tested them. Only 6-digit number are accepted (if 0, it's ok since it means that the user just need to get less than 9x documents).
Goal: if the user did one mistake, a message "Error in the number" at the place of the "0" has to appear right next to the concerned and "faulty" entry.
Everything will be activated through a button.
What I tried: with a list, but it doesn't work.
How can I change the Label dynamically created by the loop ?
user_entries=[]
error_list_length=[0,0,0,0,0,0,0,0,0]
error_list_letters=[0,0,0,0,0,0,0,0,0]
error_calculation_list=[0,0,0,0,0,0,0,0,0]
nbofcalc=9
a=0
b=1
ddd=791250
#------ Function to check if the entered calculation number are composed of 6 characters (mandatory)
def check_calc_number():
global gg_error
gg=0
gg_error=0
while gg<nbofcalc:
if len(user_entries[gg].get()) != 6:
if len(user_entries[gg].get()) ==0:
gg_error+=0
error_list_length[gg]=0
else:
gg_error+=1
error_list_length[gg]=1
else:
gg_error+=0
error_list_length[gg]=0
gg+=1
#------ Function to check if the entered calculation number contains a or many letters (prohibited)
def check_calc_letter():
global hh_error
hh=0
hh_error=0
alphabet_x='a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
for x in range(nbofcalc):
for n in user_entries[hh].get():
if n in alphabet_x:
hh_error+=1
error_list_letters[hh]=1
hh+=1
#------ Function to check with entries has an error and update the list "error_calculation_list" to try to displays the message "Error in the number" next to the concerned entries
def error_length_letters_display():
ww=0
while ww<nbofcalc:
if error_list_length[ww]==1 :
error_calculation_list[ww]="Error"
var_calculation.set(error_calculation_list[ww])
if error_list_letters[ww]==1:
error_calculation_list[ww]="Error"
var_calculation.set(error_calculation_list[ww])
ww+=1
#----- Loop to crate the entries and Label
for x in range(nbofcalc):
cadre1=Frame(fenetre)
cadre1.pack(side=TOP,anchor=NW)
cadre=Frame(cadre1)
cadre.pack()
c=str(b)
calc=Label(cadre,text="Calculation "+c+" ")
calc.pack(side=LEFT)
var_entry=StringVar()
my_entry=Entry(cadre,textvariable=var_entry, bd=5)
my_entry.insert(0,ddd)
my_entry.pack(side=LEFT)
var_calculation=StringVar()
var_calculation.set(error_calculation_list[a])
calc_error_frame=Label(cadre, textvariable=var_calculation) # The Label to change if error
calc_error_frame.pack(side=RIGHT)
user_entries.append(my_entry)
a+=1
b+=1
ddd+=1
Thank you !
You can store values and objects you need to change, so then you can change them dinamically. See this example
from tkinter import *
r = Tk()
my_entries = []
for i in range(5):
e = Label(r, text=i)
my_entries.append(e)
e.pack(side='top')
r.after(4000, lambda: my_entries[2].configure(text='Example'))
r.mainloop()
EDIT 1:As TheLizzard and Cool Cloud pointed out, it's better to avoid using time.sleep() while using tkinter. Replaced with non-blocking after()
you could setup the trace function for tk.StringVar(), when user enter any value into Entry, it will be checked. for example as shown on below user only can type decimal, and you and setup length as well.
def create_widgets(self):
self.vars = tk.StringVar()
self.vars.trace('w', partial(self.validate2, 1, 1))
# Min Voltage Validating Part
self.vars1 = tk.StringVar()
self.vars1.trace('w', partial(self.validate2, 2, 2))
# Max Voltage Validating Part
self.vars2 = tk.StringVar()
self.vars2.trace('w', partial(self.validate2, 3, 4))
# Current Validating Part
self.vars3 = tk.StringVar()
self.vars3.trace('w', partial(self.validate2, 4, 3))
# Channel Validating Part
# function( key, size)
self.enter_info = tk.Label(self, text="Please enter your information: ", bg="lightgrey")
self.enter_info.grid(tke_Table_EnterInfo)
self.voltage = tk.Label(self)
self.voltage["text"] = "MinVoltage"
self.voltage.grid(tke_Label_MinVoltage)
self.voltageInput = tk.Entry(self, width=10, textvariable=self.vars).grid(tke_StringBox_MinVoltage)
self.vars.set(0)
# Min Voltage Validating Part
self.current = tk.Label(self)
self.current["text"] = "MaxVoltage"
self.current.grid(tke_Label_MaxVoltage)
self.currentInput = tk.Entry(self, width=10, textvariable=self.vars1).grid(tke_StringBox_MaxVoltage)
self.vars1.set(5)
# Max Voltage Validating Part
self.power = tk.Label(self)
self.power["text"] = "Current"
self.power.grid(tke_Label_MaxCurrent)
self.powerInput = tk.Entry(self, width=10, textvariable=self.vars2).grid(tke_StringBox_MaxCurrent)
self.vars2.set(62.5)
# Max Current Validating Part
self.channel = tk.Label(self)
self.channel["text"] = "channel"
self.channel.grid(tke_Label_Channel)
self.channelInput = tk.Entry(self, width=10, textvariable=self.vars3).grid(tke_StringBox_Channel)
self.vars3.set(8)
# Max Channel Validating Part
def validate2(self, key, size, *args):
# TODO:add more information
if key == 1:
value = self.vars.get()
elif key == 2:
value = self.vars1.get()
elif key == 3:
value = self.vars2.get()
else:
value = self.vars3.get()
if not value.isdecimal():
print(len(value))
# if len(value) < 2:
corrected = ''.join(filter(str.isdecimal, value))
if key == 1:
self.vars.set(corrected)
elif key == 2:
self.vars1.set(corrected)
elif key == 3:
self.vars2.set(corrected)
else:
self.vars3.set(corrected)
if key == 1:
corrected = self.vars.get()
corrected = corrected[0:size]
self.vars.set(corrected)
elif key == 2:
corrected = self.vars1.get()
corrected = corrected[0:size]
self.vars1.set(corrected)
elif key == 3:
corrected = self.vars2.get()
corrected = corrected[0:size]
self.vars2.set(corrected)
else:
corrected = self.vars3.get()
corrected = corrected[0:size]
self.vars3.set(corrected)

Tkinter/Python hanging on user response if response is the last option in a while or if loop

The following code works for requesting input from a user through the Tkinter GUI and turning that input into a usable variable in the main script. However, any value that I put as the last in a list in the if statement (here "4") will hang and crash the program upon enter. This was also the case for "n" in a yes/no scenario. It also happens if I replace the if statement with a while not in [values] - the final value will crash the program. Is this just a quirk of Tkinter or is there something that I am missing?
import tkinter as tk
from tkinter import *
# get choice back from user
global result
badinput = True
while badinput == True:
boxwidth = 1
result = getinput(boxwidth).strip().lower()
if result in ['1', '2', '3', '4']:
badinput = False
# iterate through play options
if result == '1':
# Do Something
elif result =='2':
# Do Something
elif result =='3':
# Do Something
else:
# Do Something
def getinput(boxwidth):
# declaring string variable for storing user input
answer_var = tk.StringVar()
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
return
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(answer_var)
answer_entry.destroy()
return answer_var.get()
In case anyone is following this question, I did end up solving my problem with a simple if statement within the callback. I can feed a dynamic "choicelist" of acceptable responses into the callback upon user return. If the answer is validated, the gate_var triggers the wait function and sends the program and user response back into the program.
'''
def getinput(boxwidth, choicelist):
# declaring string variable for storing user input
answer_var = tk.StringVar()
gate_var = tk.StringVar()
dumplist = []
# defining a function that will
# get the answer and set it
def user_response(event):
answer_var.set(answer_entry.get())
if choicelist == None:
clearscreen(dumplist)
gate_var.set(answer_entry.get())
return
if answer_var.get() in choicelist:
# passes a validated entry on to gate variable
clearscreen(dumplist)
gate_var.set(answer_entry.get())
else:
# return to entry function and waits if invalid entry
clearscreen(dumplist)
ErrorLabel = tk.Label(root, text = "That is not a valid response.")
ErrorLabel.pack()
ErrorLabel.config(font = ('verdana', 18), bg ='#BE9CCA')
dumplist.append(ErrorLabel)
return
global topentry
if topentry == True:
answer_entry = tk.Entry(top, width = boxwidth, borderwidth = 5)
else:
answer_entry = tk.Entry(root, width = boxwidth, borderwidth = 5)
# making it so that enter calls function
answer_entry.bind('<Return>', user_response)
# placing the entry
answer_entry.pack()
answer_entry.focus()
answer_entry.wait_variable(gate_var)
answer_entry.destroy()
return answer_var.get()
'''

Command function for button "resets"

So in my tkinter python program I am calling on a command when a button is clicked. When that happens it runs a function but in the function I have it set a label to something on the first time the button is clicked and after that it should only update the said label. Basically after the attempt it changes the attempt to 1 ensuring the if statement will see that and not allow it to pass. However it keeps resetting and I don't know how to stop it. When you click the button no matter first or third the button resets and proof of that occurs because the h gets printed. It's as if the function restarts but it shouldn't since it's a loop for the GUI.
def fight(): #Sees which one is stronger if user is stronger he gets win if no he gets loss also displays enemy stats and removes used characters after round is finished
try:
attempt=0
namel = ""
namer=""
left = lbox.curselection()[0]
right = rbox.curselection()[0]
totalleft = 0
totalright = 0
if left == 0:
namel = "Rash"
totalleft = Rash.total
elif left==1:
namel = "Untss"
totalleft = Untss.total
elif left==2:
namel = "Illora"
totalleft = 60+35+80
if right == 0:
namer = "Zys"
totalright = Zys.total
elif right==1:
namer = "Eentha"
totalright = Eentha.total
elif right==2:
namer = "Dant"
totalright = Dant.total
lbox.delete(lbox.curselection()[0])
rbox.delete(rbox.curselection()[0])
print(namel)
print(namer)
if attempt == 0:
wins.set("Wins")
loss.set("Loss")
print("h")
attempt=1
if (totalleft>totalright):
wins.set(wins.get()+"\n"+namel)
loss.set(loss.get()+"\n"+namer)
else:
wins.set(wins.get()+"\n"+namer)
loss.set(loss.get()+"\n"+namel)
except IndexError:
pass
Also for those of you who saw my previous question I still need help with that I just also want to fix this bug too.
At beginning of function fight you set attempt = 0 so you reset it.
Besides attempt is local variable. It is created when you execute function fight and it is deleted when you leave function fight. You have to use global variable (or global IntVar)
attempt = 0
def fight():
global attempt
BTW: of you use only values 0/1 in attempt then you can use True/False.
attempt = False
def fight():
global attempt
...
if not attempt:
attempt = True

Individual keypress bug in python

The following code is a python sprinting game. It was posted as an answer to my previous post, by #mango You have to tap 'a' and 'd' as fast as you can to run 100 meters. However, there are a few bugs...
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
Previous code:
import msvcrt
import time
high_score = 50
name = "no-one"
while True:
distance = int(0)
print("\n--------------------------------------------------------------")
print('\n\nWelcome to the 100m sprint, tap a and d rapidly to move!')
print('* = 10m')
print("\n**Current record: " + str(high_score) + "s, by: " + name)
print('\nPress enter to start')
input()
print('Ready...')
time.sleep(1)
print('GO!')
start_time = time.time()
while distance < 100:
k1 = msvcrt.getch().decode('ASCII')
if k1 == 'a':
k2 = msvcrt.getch().decode('ASCII')
if k2 == 'd':
distance += 1
if distance == 50:
print("* You're halfway there!")
elif distance % 10 == 0:
print('*')
fin_time = time.time() - start_time
fin_time = round(fin_time,2)
print('Well done you did it in...'+str(fin_time))
if fin_time < high_score:
print("Well done you've got a new high score ")
name = input("Please enter your name : ")
This is the code that I recieved as feedback, I have made a few changes, but I am struggling witht the bugs that I listed before.
1) If you hold down 'a' and 'd' at the same time, the game is completed as fast as possible and defeats the point of the game.
2) Like in my previous post, I would like to incorporate a scoring system into this code, however, due to my level of skill and experience with python, unfortunately, I am unable to do so, and would appreciate and suggestions.
Many Thanks
# these are the modules we'll need
import sys
import tkinter as tk
class SprintGame(object):
# this represents the distance we'll allow our player to run
# NOTE: the distance is in meters
distance = 100
# this represents the stride length of the players legs
# NOTE: basically how far the player can travel in one footstep
stride = 1.5
# this represents the last key the user has pressed
lastKey = ""
# this represents wether or not the race has been completed
completed = False
# this function initiates as soon as the "sprint" variable is defined
def __init__(self):
# create the tk window
self.root = tk.Tk()
# set the tk window size
self.root.geometry('600x400')
# set the tk window title
self.root.title("Sprinting Game")
# bind the keypress event to the self.keypress handler
self.root.bind('<KeyPress>', self.keypress)
# center the window
self.centerWindow(self.root)
# insert the components
self.insertComponents()
# initial distance notice
self.output("{0}m left to go!".format(self.distance))
# start out wonderful game
self.start()
# this function centers the window
def centerWindow(self, window):
window.update_idletasks()
# get the screen width
width = window.winfo_screenwidth()
# get the screen height
height = window.winfo_screenheight()
# get the screen size
size = tuple(int(_) for _ in window.geometry().split('+') [0].split('x'))
# get the screen's dimensions
x = (width / 2) - (size[0] / 2)
y = (height / 2) - (size[1] / 2)
# set the geometry
window.geometry("%dx%d+%d+%d" % (size + (x, y)))
# this function replaces the old text in the textbox with new text
def output(self, text = ""):
self.text.delete('1.0', tk.END)
self.text.insert("end", text)
# this function handles key presses inside the tkinter window
def keypress(self, event):
# get the key and pass it over to self.logic
self.logic(event.char)
# this function handles game logic
def logic(self, key):
# convert key to a lower case string
key = str(key).lower()
# let us know how far we've got left
if key == "l":
self.output("{0}m left to go!".format(self.distance))
# restart the race
if key == "r":
# clear the output box
self.text.delete('1.0', tk.END)
# reset the distance
self.distance = 100
# reset the stride
self.stride = 1.5
# reset the last key
self.lastKey = ""
# set race completed to false
self.completed = False
# output restart notice
self.output("The Race Has Been Restarted.")
# don't bother with logic if race is completed
if self.completed == True:
return False
# check if distance is less than or equal to zero (meaning the race is over)
if self.distance <= 0:
# set the "self.completed" variable to True
self.completed = True
# let us know we've completed the race
self.output("Well done, you've completed the race!")
# return true to stop the rest of the logic
return True
# convert the key to lower case
key = key.lower()
# this is the quit function
if key == "q":
# lights out...
sys.exit(0)
# check if the key is a
if key == "a":
# set the last key to a so that we can decrement the "distance"
# variable if it is pressed next
self.lastKey = "a"
# only bother with "d" keypresses if the last key was "a"
if self.lastKey == "a":
# capture the "d" keypress
if key == "d":
# decrement the "distance" variable
self.distance -= self.stride
# let us know how far into the game we are
self.output("{0}m left to go!".format(self.distance))
# this function inserts the components into the window
def insertComponents(self):
# this component contains all of our output
self.text = tk.Text(self.root, background='#d6d167', foreground='#222222', font=('Comic Sans MS', 12))
# lets insert out text component
self.text.pack()
# this function opens the window and starts the game
def start(self):
self.root.mainloop()
# create a new instance of the game
Game = SprintGame()

Changing property of a QLE in python

I'm currently having an issue with a QLE that I created. I would like the qle to take a value, turn it into a float and then increase or decrease a label value depending on if the value is positive or negative. The only problem is is that every time I type something into the qle it will start adding that value to the label before I'm done typing. For example: I type in "4" into the qle that works but once I type in "4." anything it will read it as 4 two times so the label will be changed to 8. Maybe there's a way so that when I press a button it will increase or decrease but only after I press the button?
I added code for a button I created and maybe it would be easier to link that with the qle. Many thanks!
#this creates the increase button for cam1 focus
self.btnCam1IncreaseFocus = QtGui.QPushButton("+",self)
self.btnCam1IncreaseFocus.clicked.connect(self.cam1IncreaseFocus)
self.btnCam1IncreaseFocus.resize(25,25)
self.btnCam1IncreaseFocus.move(75,100)
#This creates a textbox or QLE for a custom tweak value for cam1 focus
self.qleTextBoxCam1Focus = QtGui.QLineEdit(self)
self.qleTextBoxCam1Focus.resize(25,25)
self.qleTextBoxCam1Focus.move(40,100)
self.qleTextBoxCam1Focus.textChanged[str].connect(self.qleCam1Focus)
def cam1IncreaseFocus(self):
text = self.lblCam1Focus.text()
n = float(text)
n = n + 1
self.lblCam1Focus.setText(str(n))
def qleCam1Focus(self):
text = self.qleTextBoxCam1Focus.text()
if text == "":
text = "0.0"
if str(text).isalpha() == False:
n = float(text)
textLabel = self.lblCam1Focus.text()
if textLabel == "":
textLabel = "0.0"
y = float(textLabel)
result = n + y
if result <= 0.0:
result = 0.0
self.lblCam1Focus.setText(str(result))
Instead of textChanged, use the editingFinished signal, which will only fire when return/enter is pressed, or when the line-edit loses focus:
self.qleTextBoxCam1Focus.editingFinished.connect(self.qleCam1Focus)

Categories