I am trying to make my first GUI script based on the answers of 2 questions. I'll show an example of the non GUI script
while True:
ammount = input("What is your debt")
if ammount.isdigit() == True:
break
else:
print("Please try typing in a number")
while True:
month = input("In which month did the debt originate?")
if month not in ["January", "February"]:
print("Try again")
else:
break
The point of this script is that it is scalable with all the questions one may add, I want to understand it in the same way in Tkinter. I'll show what I've tried:
from tkinter import *
def click():
while True:
entered_text = text_entry.get()
if entered_text .isdigit() == False:
error = Label(window, text="Try again", bg = "black", fg="red", font="none 11").grid(row = 3, column = 2, sticky= W).pack()
else:
break
return True
window = Tk()
window.title("Tax calculator")
window.configure(background="black")
monto = Label(window, text="¿What is your debt?", bg="black", fg="white", font="none 12 bold")
monto.grid(row = 1, column = 0, sticky= W)
text_entry = Entry(window, width = 20, bg="white")
text_entry.grid(row = 2, column=2, sticky=W)
output = Button(window, text = "Enter", width = 6, command = click)
output.grid(row = 3, column = 0, sticky = W)
The thing is, I can't add a Label() method in the if/else of the click() method, because I would like to ask a new question once the condition is met. I can't also can't get True from click once the condition is met, because the input comes from Button() method. Thanks in advance
You don't actually need any loops here, a simple if statement would be enough to do the trick. Also, there is no need to recreate the label every time, you can just configure() its text. And, note that indexing starts from 0 - so, in your grid, actual first row (and column) needs to be numbered 0, not 1. Besides that, I suggest you get rid of import *, since you don't know what names that imports. It can replace names you imported earlier, and it makes it very difficult to see where names in your program are supposed to come from. You might want to read what PEP8 says about spaces around keyword arguments, as well:
import tkinter as tk
def click():
entered_text = text_entry.get()
if not entered_text.isdigit():
status_label.configure(text='Please try again')
text_entry.delete(0, tk.END)
else:
status_label.configure(text='Your tax is ' + entered_text)
text_entry.delete(0, tk.END)
root = tk.Tk()
root.title('Tax calculator')
root.configure(background='black')
monto = tk.Label(root, text='¿What is your debt?', bg='black', fg='white', font='none 12 bold')
monto.grid(row=0, column=0, padx=10, pady=(10,0))
text_entry = tk.Entry(root, width=20, bg='white')
text_entry.grid(row=1, column=0, pady=(10,0))
status_label = tk.Label(root, text='', bg='black', fg='red', font='none 11')
status_label.grid(row=2, column=0)
button = tk.Button(root, text='Enter', width=17, command=click)
button.grid(row=3, column=0, pady=(0,7))
root.mainloop()
I forgot to mention, if your application gets bigger, you might be better off using a class.
Related
I'm writing a simple unit converter where the user can pick which units they want to convert from two options. I'm using radio-buttons for the choice, but can't seem to get the value of the chosen one to work in the conditions at the bottom of the program.
I tried several solutions suggested here on stack overflow, but none of them worked. At one point, I got the selected() to print the value of the button, but it still didn't work in the condition. Am I missing something obvious here?
Please, note, the converter is not finished yet, there is still some more polishing to do after this issue is solved.
from tkinter import *
window = Tk()
window.title("Unit converter")
window.minsize(width=300, height=300)
window.config(padx=50, pady=50)
def lbs_kgs():
user_input = float(unit_A1.get())
result = round((user_input / 2.2046), 2)
unit_B1.config(text= f"{result}")
def mil_km():
user_input = float(unit_A1.get())
result = round((user_input * 1.6), 2)
unit_B1.config(text= result)
def selected():
return radio_state.get()
intro_label = Label(text = "What units would you like to convert?")
intro_label.grid(column=0, row=0, columnspan=4, pady=10)
radio_state = StringVar()
radiobutton1 = Radiobutton(text="Pounds to kilograms", value="pk", variable=radio_state, command=selected)
radiobutton2 = Radiobutton(text="Miles to kilometers", value="mk", variable=radio_state, command=selected)
radiobutton1.grid(column=0, row=1, columnspan=4)
radiobutton2.grid(column=0, row=2, columnspan=4)
instructions_label = Label(text = "Enter the number:")
instructions_label.grid(column=0, row=3, columnspan=4, pady=10)
unit_A1 = Entry(width=5)
unit_A1.grid(column=1, row=4, sticky="e")
unit_A1_label = Label(text = "unit A1")
unit_A1_label.grid(column=2, row=4, sticky="w")
equal_label = Label(text = "is equal to")
equal_label.grid(column=1, row=5, sticky="e")
unit_B1 = Label(text = "0")
unit_B1.grid(column=2, row=5, sticky="w")
unit_B1_label = Label(text = "result unit")
unit_B1_label.grid(column=3, row=5, sticky="w")
button = Button(text="Calculate")
button.grid(column=0, row=6, columnspan=4, pady=10)
if selected() == "pk":
button.config(command=lbs_kgs)
elif selected() == "mk":
button.config(command=mil_km)
window.mainloop()
Move the if/else check into the selected function so the conditions can be checked each time the selection changes
def selected():
selection = radio_state.get()
if selection == "pk":
button.config(command=lbs_kgs)
elif selection == "mk":
button.config(command=mil_km)
In line 29 should be radio_state = StringVar(window, '1'). Without this when executed both radiobutton are on, but that not right.
def selected():
if (selection := radio_state.get()) == "pk":
button.config(command=lbs_kgs)
elif selection == "mk":
button.config(command=mil_km)
Output:
Output pound to Kilograms:
Output Miles to Kilometers:
I have 2 Entrys and one button. I want to make that button's state disabled until the two Entrys are filled in. How can I achieve that?
howManyStocksLabel = Label(root, text = "How many stocks do you want to evaluate?")
howManyStocksLabel.grid(row = 1, column = 0)
howManyStocksEntry = Entry(root, borderwidth = 3)
howManyStocksEntry.grid(row = 1, column = 1)
riskLabel = Label(root, text = "Enter risk %")
riskLabel.grid(row = 2, column = 0, sticky = 'w')
riskEntry = Entry(root, borderwidth = 3)
riskEntry.grid(row = 2, column = 1)
nextButton = Button(root, text = "Next!", width = 20, height = 2,state = DISABLED,
fg = 'green', bg = 'white',
command= lambda: myClick(riskEntry, howManyStocksEntry, var))
nextButton.grid(row = 4, column = 1)
I tried to check whether the entries are filled in or not by:
if(riskEntry.get() != ""):
....................
but it just doesn't work.
You need to check if the value is there after the user inputs it. Also, you can use tk.StringVar() as a text variable and trace it.
Here is an example:
import tkinter as tk
def check_entry(*args):
if r1.get() and r2.get():
b1.config(state='normal')
else:
b1.config(state='disabled')
root = tk.Tk()
r1 = tk.StringVar(master=root)
r2 = tk.StringVar(master=root)
e1 = tk.Entry(root, textvariable=r1)
e1.pack()
e2 = tk.Entry(root, textvariable=r2)
e2.pack()
b1 = tk.Button(root, text='Click Me!', state='disabled')
b1.pack()
r1.trace('w', check_entry)
r2.trace('w', check_entry)
root.mainloop()
You will need to use a binding on your entry widgets to check whether the user has entered anything into the entry or not.
This code will fire the check_entry function every time the user types in one of the entry boxes:
riskEntry.bind('<KeyRelease>', check_entry)
howManyStocksEntry.bind('<KeyRelease>', check_entry)
Then your check_entry function might look like this:
def check_entry(event): #event is required for all functions that use a binding
if riskEntry.get() and howManyStocksEntry.get():
nextButton.config(state=NORMAL)
else:
nextButton.config(state=DISABLED)
One way to do it would be to utilize the ability to "validate" their contents that Entry widgets support — see adding validation to an Entry widget — but make it check the contents of multiple Entry widgets and change the state of a Button accordingly.
Below shows how to do this via a helper class that encapsulates most of the messy details needed to make doing it relatively painless. Any number of Entry widgets can be "watched", so it scales well to handle forms consisting of many more than merely two entries.
from functools import partial
import tkinter as tk
from tkinter.constants import *
class ButtonEnabler:
""" Enable/disable a Button depending on whether all specified Entry widgets
are non-empty (i.e. contain at least one character).
"""
def __init__(self, button, *entries):
self.button = button
self.entries = entries
for entry in self.entries:
func = root.register(partial(self.check_entries, entry))
entry.config(validate="key", validatecommand=(func, '%P'))
def check_entries(self, this_entry, new_value):
other_entries = (entry for entry in self.entries if entry is not this_entry)
all_others_filled = all(entry.get() for entry in other_entries)
combined = bool(new_value) and all_others_filled
self.button.config(state=NORMAL if combined else DISABLED)
return True
root = tk.Tk()
howManyStocksLabel = tk.Label(root, text="How many stocks do you want to evaluate?")
howManyStocksLabel.grid(row=1, column=0)
howManyStocksEntry = tk.Entry(root, borderwidth=3)
howManyStocksEntry.grid(row=1, column=1)
riskLabel = tk.Label(root, text="Enter risk %")
riskLabel.grid(row=2, column=0, sticky='w')
riskEntry = tk.Entry(root, borderwidth=3)
riskEntry.grid(row=2, column=1)
nextButton = tk.Button(root, text="Next!", width=20, height=2, state=DISABLED,
fg='green', bg='white', disabledforeground='light grey',
command=lambda: myClick(riskEntry, howManyStocksEntry, var))
nextButton.grid(row=4, column=1)
enabler = ButtonEnabler(nextButton, howManyStocksEntry, riskEntry)
root.mainloop()
I'm creating a 20 questions game which requires a NEW random value to be created for the "answer". I'm having trouble as if I put the randomization OUTSIDE of the command loop, it doesn't change, and if I put it INSIDE it doesn't carry....
Any recommendations?
I've deleted all the excess code.
I've tried the random only inside, only outside, and in both, none of which worked. Any other count attempts I've tried have failed.
Prior to trying to change the randomizer, everything functioned correctly.
I considered trying to add 2 buttons, but it just wasn't what I was looking for...
window = Tk()
window.title("Welcome to LikeGeeks app")
window.geometry('400x200')
answers = ('dog','cat','rock','aki','table','flower','paris','florida', 'jackie chan')
#deleted section of all the string lists start
#deleted section of all the string lists end
aki = Label(window, text="Aki: ", font=("Tahoma", 14))
aki.grid(column=0, row=0)
sp1 = Label(window, text="Let's play 20 Questions, and you guess first.", fg="blue", font=("Tahoma", 14))
sp1.grid(column=1, row=0, sticky=W)
sp2 = Label(window, text="Try to only say one word answers.", fg="blue", font=("Tahoma", 14))
sp2.grid(column=1, row=1, sticky=W)
sp3 = Label(window, text="If that's alright, type \"okay\"", fg="blue", font=("Tahoma", 14))
sp3.grid(column=1, row=2, sticky=W)
ent = Entry(window,width=15)
ent.grid(column=1, row=3, sticky=W)
#original location of the answertrue = random segment
count = 0
#working that button
def clicked():
global count
guess = str(ent.get())
count = count + 1
if count == 1:
sp1.configure(text="Then let's begin!")
sp2.configure(text="Make your first guess.")
sp3.configure(text=" ")
answertrue = random.choice(answers)
#Restart the program
if count == 20:
sp1.configure(text="You took too long!")
sp2.configure(text="Click Sart to choose a new word,")
sp3.configure(text="or click enter for another 20 guesses!")
count = 0
if count == 2:
#this was all the base elifs for seeing if you guessed correctly, it was obnoxiously long, so I deleted it.
btn = Button(window, text="Enter", bg="white", fg="green", command=clicked)
btn.grid(column=0, row=4)
I hope that I can just get it to set back the count to 0 and create a new random value for answertrue from the list answers to come out.
I am using Tkinter in python 3.4 to make a text based game, and I cannot figure out how to get a string from an Entry widget, it just returns Py_Var#, # being a number. I have looked at answers to similar questions, but none of them quite line up with what I need and have. Here's the relevant pieces of code:
from tkinter import *
win = Tk()
win.geometry("787x600")
playername = StringVar()
def SubmitName():
playername.get
#messagebox.showinfo("Success", playername)
print(playername)
frame3 = Frame(win)
frame3.pack()
label1 = Label(frame3, text="You awaken in a room, with no memories of yourself or your past. ")
label2 = Label(frame3, text="First, how about you give yourself a name:")
label1.config(font=("Courier", 11))
label2.config(font=("Courier", 11))
entry1 = Entry(frame3, textvariable=playername)
entry1.config(font=("Courier", 11))
label1.grid(row=0, column=0, columnspan=3)
label2.grid(row=1, column=0)
entry1.grid(row=1, column=1)
bnamesub= Button(frame3, text="Submit", command=lambda: SubmitName())
bnamesub.grid()
win.mainloop()
Also, first time using stackoverflow and its reading weird but w/e.
You have two mistakes in SubmitName().
First, you need to get the text like this:
txt = playername.get()
Then you need to print that txt:
print(txt)
By mistake you printed the StringVar variable itself.
from tkinter import *
import pickle
win = Tk()
win.geometry("787x600")
def SubmitName():
playername = entry1.get()
messagebox.showinfo("Success", playername)
print(playername)
frame3 = Frame(win)
frame3.grid()
label1 = Label(frame3, text="You awaken in a room, with no memories of yourself or your past. ")
label2 = Label(frame3, text="First, how about you give yourself a name:")
label1.config(font=("Courier", 11))
label2.config(font=("Courier", 11))
#name entered is a StringVar, returns as Py_Var7, but I need it to return the name typed into entry1.
entry1 = Entry(frame3)
entry1.config(font=("Courier", 11))
label1.grid(row=0, column=0, columnspan=3)
label2.grid(row=1, column=0)
entry1.grid(row=1, column=1)
bnamesub= Button(frame3, text="Submit", command=lambda: SubmitName())
bnamesub.grid()
What I changed:
-deleted playername = StringVar(). We don't really need it;
-changed inside the function: changed playername.get to playername = entry1.get();
-added frame3.grid() (without geometry managment, widgets cannot be shown on the screen.);
-also, a little edit: in Python, comments are created with # sign. So I changed * to #.
I was happy to find a solution here, but all these answers "as it is" are not working with my setting, python3.8, pycharm 2018.2
So if anyone could answer this, it seems that entry1.get() cannot be used as a string. I first wanted to append it in a list, and I did a more simple version to point out the trouble :
from tkinter import *
import pickle
win = Tk()
win.geometry("300x300")
#playername = StringVar()
def SubmitName():
labell = Label(win, text="Little tryup").grid()
playername = entry1.get()
# result about line 11: 'NoneType' object has no attribute 'get'
labelle = Label(win, text=playername).grid()
# print(txt)
label1 = Label(win, text="Enter a name:").grid()
entry1 = Entry(win).grid()
boutonne = Button(win, text="label-it!", command=lambda: SubmitName())
boutonne.grid()
win.mainloop()
from tkinter import *
root = Tk()
root.title("Tip & Bill Calculator")
totaltxt = Label(root, text="Total", font=("Helvitca", 16))
tiptxt = Label(root, text="Tip (%)", font=("Helvitca", 16))
peopletxt = Label(root, text="people", font=("Helvitca", 16))
totaltxt.grid(row=0, sticky=E)
tiptxt.grid(row=1, sticky=E)
peopletxt.grid(row=2, sticky=E)
totalentry = Entry(root)
tipentry = Entry(root)
peopleentry = Entry(root)
totalentry.grid(row=0, column=2)
tipentry.grid(row=1, column=2)
peopleentry.grid(row=2, column=2)
ans = Label(root, text = "ANS")
ans.grid(row=4)
def answer(event):
data1 = totalentry.get()
data2 = tipentry.get()
data3 = peopleentry.get()
if tipentry.get() == 0:
ans.configure(str((data1/data3)), text="per person")
return
elif data1 == 0:
ans.configure(text="Specify the total")
return
elif data3 == 0 or data3 ==1:
ans.configure(str(data1*(data2/100+1)))
return
elif data1 == 0 and data2 == 0 and data3 ==0:
ans.configure(text = "Specify the values")
return
else:
ans.configure(str((data1*(data2/100+1)/data3)), text="per person")
return
bf = Frame(root)
bf.grid(row=3, columnspan=3)
calc = Button(bf, text ="Calculate", fg = "black", command = answer)
calc.bind("<Button-1>", answer)
calc.grid(row=3, column=2)
root.mainloop()
I'm trying to make a tip and bill calculator with a simple design just to learn and experiment. However, I encountered a horrible problem that kept haunting for days, I usually struggle with functions in python and I'm trying to bind a function to a calculate button, which I managed to make it appear. However, I can't manage to get it to work. After some messing around I ended with this error, when I click the calculate button.
This is the error after I click the calculate button:
TypeError: answer() missing 1 required positional argument: 'event'
Commands bound to a button do not get an argument, as the nature of the event is already known. Delete 'event'.
You also bind the answer function to an event. The result is that answer is called both without and with an event argument. Get rid of the bind call.
Follow hint given by Bryan. Stop passing a digit string to .configure as a positional parameter. tk will try to interpret is as dictionary. Instead, add the number string to the rest of the label string.
Like rows, columns start from 0.
The frame is not needed.
The following revision works.
from tkinter import *
root = Tk()
root.title("Tip & Bill Calculator")
totaltxt = Label(root, text="Total", font=("Helvitca", 16))
tiptxt = Label(root, text="Tip (%)", font=("Helvitca", 16))
peopletxt = Label(root, text="people", font=("Helvitca", 16))
totaltxt.grid(row=0, column=0, sticky=E)
tiptxt.grid(row=1, column=0, sticky=E)
peopletxt.grid(row=2, column=0, sticky=E)
totalentry = Entry(root)
tipentry = Entry(root)
peopleentry = Entry(root)
totalentry.grid(row=0, column=1)
tipentry.grid(row=1, column=1)
peopleentry.grid(row=2, column=1)
ans = Label(root, text = "ANS")
ans.grid(row=4, column=0, columnspan=2, sticky=W)
def answer():
total = totalentry.get()
tip = tipentry.get()
people = peopleentry.get()
if not (total and tip):
ans['text'] = 'Enter total and tip as non-0 numbers'
else:
total = float(total)
tip = float(tip) / 100
people = int(people) if people else 1
ans['text'] = str(round(total * tip / people, 2)) + " per person"
calc = Button(root, text ="Calculate", fg = "black", command = answer)
calc.grid(row=3, column=1)
root.mainloop()