I am new to python and this is the first project I am working on, it is a Litecoin - Euro conversion program. I had a problem, that if I did not put anything into the Entry() fields of the program and submitted it, the console would output following error message (because int() and float() cannot convert an empty string into an int/ a float):
Exception in Tkinter callback
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1536, in __call__
return self.func(*args)
File "/Users/Samir/Library/Mobile Documents/com~apple~CloudDocs/Coding/Python/hello_world/exch.py", line 13, in exchange
choice = int( exc.get() ) # choice of EUR to LTC or other way around
ValueError: invalid literal for int() with base 10: ''
The whole code I used in that matter:
from Tkinter import *
from json import *
from urllib2 import *
def exchange():
data = urlopen('https://btc-e.com/api/3/ticker/ltc_eur') # EUR - LTC API
j_obj = load(data) #loads json data
exch = float( j_obj['ltc_eur']['avg'] ) # picks exchange rate out of api
choice = int( exc.get() ) # choice whether EUR to LTC or other way around
amount = float ( am.get() ) # amount of the currency the user wants to convert
if choice == 0:
print round((1 / exch) * amount, 2), "Litecoins"
elif choice == 1:
print round(exch * amount, 2), "Euro"
else: # if something other than 0 or 1 was typed in
print "I'm sorry but you have to enter either 0 or 1"
am.delete(0, END)
exc.delete(0, END)
master = Tk() # creates new window in var master
master.wm_title("LTC - EUR converter") # creates title for the window
Label(master, text = "EUR into LTC (0) or LTC into EUR(1): ").grid(row = 0, sticky = W) # creates choice string in a table (Row 1)
Label(master, text = "Amount: ").grid(row = 1, sticky = E) # creates text "Amount" in a table (Row 1)
exc = Entry(master) # picks up Entryvalue
exc.grid(row = 0, column = 1)
am = Entry(master) # picks up Entryvalue
am.grid(row = 1, column = 1) # places it at row 0 colum 1 in the table
Button(master, text = "Quit", command = master.quit).grid(row = 2, column= 1, sticky = W) # creates a quit button that closes the window
Button(master, text = "Submit", command = exchange).grid(row = 2, column = 1) # creates a submit button that executes def "exchange"
mainloop() # starts the program
I managed to fix the problem with changing the if query to comparing the input to strings and converting the input (to int and float) after it confirmed either 0 or 1 has been typed in.
The changed code:
choice = exc.get()
amount = am.get()
if choice == '0':
choice = int(choice)
amount = float(amount)
print round((1 / exch) * amount, 2), "Litecoins"
elif choice == '1':
choice = int(choice)
amount = float(amount)
print round(exch * amount, 2), "Euro"
else:
print "I'm sorry but you have to enter either 0 or 1"
So my question is, is there any solution for that problem that is more efficient? Because I think my approach works but it isn't the best option out there.
"It is better to ask for forgiveness than permission".
You can place your code in a try-except block.
try:
choice = int(choice)
amount = float(amount)
except ValueError:
print "I'm sorry but you have to enter either 0 or 1"
return
if choice == 1:
...
else:
...
This way, you check the value of choice once rather than twice.
Edit: If would be prudent to keep separate try-except braces for choice and amount. With the current scenario, entering a valid choice but an invalid amount could yield a misleading error message.
Related
The following code searches a text file for a name and displays the related number in a tkinter entry box in Python.
so original text file includes:
bob 19
dan 20
shayne 17
I would like add another nested loop so that if there are two names the same then two numbers are returned to the entry box. Sorry, I am new to Python, have tried but always come up with an error.
bob 18
bob 19
dan 20
shayne 17
#https://www.youtube.com/watch?v=lR90cp1wQ1I
from tkinter import *
from tkinter import messagebox
race = []
def displayInfo(race, name):
found = False
pos = 0
while pos < len(race) and not found:
if race[pos][0] == name:
found = True
pos+=1
if found:
return race[pos-1][1]
else:
messagebox.showerror(message = "Invalid input, please try again.")
def clickArea():
fin.set(displayInfo(race, name.get()))
def createlist():
raceFile = open ("C:/python/files/number_list.txt", 'r')
for line in raceFile:
racer = line.split()
race.append(racer)
raceFile.close()
return race
root = Tk()
root.title("Read From text File/List GUI")
Label(root, text="Name").grid(row=0, column=0)
name = StringVar()
Entry(root, textvariable=name).grid(row=0, column =1)
Label(root, text="Finish Time").grid(row=2, column=0)
fin=IntVar()
Label(root, textvariable=fin).grid(row=2, column=1)
button = Button(root, text="Finish Time", command=clickArea)
button.grid(row=3, column=0, columnspan=2)
createlist()
print(race)
your question is not related to tkinter, so I made the code without it.
It works like this: you enter the name you're looking for, then it looks for matches using the count method. If there is a match, then the index is written to the 'B' array. Further, since there is a space between the name and number, we take the space index + 1 and start outputting the string from this position to the end.
name = input('who do you want to find: ') + " "
with open("number_list.txt", "r") as file:
A = file.readlines()
#remove program entry '\n'
for i in range(len(A)):
A[i] = A[i].strip()
#getting matching names
B = [] #the court records the names we need
for i in A:
if i.count(name): #truth check
#this notation is equivalent to the notationsi: if i.count(name) == 1:
B.append(i)
print('the following numbers match:')
for i in B:
index_space = i.index(' ') + 1
print(i[index_space:])
If you want to get all the values for a name, you need to go through all the items in race:
def displayInfo(race, name):
# go through the race list and return values for given name
found = [x[1] for x in race if x[0] == name]
if found:
# return the values separated by comma
return ", ".join(found)
# no item found for the given name
return "not found"
So basically, I am trying to create a python program to detect whether the 3 digit number entered by the user is an Armstrong number or not. It's working fine in python, except it's printing the answer 3 times in terminal. But when I use tkinter, I get some problems since I basically just got to know by it just a few hours ago and don't really know how to use it. The problem I get is in > operator, I'm receiving data in Entry() but > operator is for integers so it is giving me type errors.
Here's the error
TypeError: '>' not supported between instances of 'int' and 'Entry'
Here is my code
import re
from tkinter import *
root = Tk()
label = Label(root, text="Enter a 3 digit Number")
label.pack()
num = Entry(root, width=50, bg="orangered")
num.pack()
def myclick():
Sum = 0
temp = num
if 1000 > num > 99:
while temp > 0:
digit = temp % 10
Sum += digit ** 3
temp //= 10
if num == Sum:
label2 = Label(root, num, "is an Armstrong number")
label2.pack()
else:
label3 = Label(root, num, "is not an Armstrong number")
label3.pack()
else:
label4 = Label(root,
"The number you entered is not a 3 digit number, Please Enter a number between 100 and 999")
label4.pack()
MyButton = Button(root, text="Click", command=myclick)
MyButton.pack()
root.mainloop()
All I want is to make this program work in a gui, and stop printing the result thrice. Can anyone help me?
As num is an Entry widget, you cannot use it directly in value comparison. Use int(num.get()) to convert the content to an integer number. You also need to cater invalid input, for example something like "abc", using try / except.
Also you create new label (for showing the result) in each iteration of the while loop, that is why you get 3 labels for each checking. It is better to create the result label once outside myclick() and update it inside the function instead.
Below is the modified myclick():
def myclick():
try:
number = num.get()
value = int(number)
if 999 >= value >= 100:
total = sum(int(digit)**3 for digit in number)
message = f"{value} {'is' if value == total else 'is not'} an Armstrong number"
else:
message = "Enter a number between 100 and 999"
except ValueError:
message = f"'{number}' is not a valid number"
result.config(text=message) # show the result
And create result label outside the function:
result = Label(root)
result.pack()
I know my question sounds very much like many previous questions but I can honestly not figure it out in the context of my program. I have an algorithm for the Collatz Conjecture that I would like to run through a Tkinter GUI (everything works just fine through the terminal).I have tried to bind the relevant function to the Return key and to a button but I get the same error message for both methods of entering data, which I will show below. I get the output to work perfectly on the GUI if I input through the terminal.
What I have tried is best explained through the code below. (The code above the #### line has mostly to do with making the GUI appear over my Spyder IDE and not hiding behind it.)
Code:
from tkinter import *
root = Tk()
import os
import subprocess
import platform
def raise_app(root: Tk):
root.attributes("-topmost", True)
if platform.system() == 'Darwin':
tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {} to true'
script = tmpl.format(os.getpid())
output = subprocess.check_call(['/usr/bin/osascript', '-e', script])
root.after(0, lambda: root.attributes("-topmost", False))
########################################################################
lst = []
def collatz(num):
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
def main(event):
collatz(num)
#Input Box
input = Entry(root, width = 10, bg = "light grey")
input.grid(row = 0, column = 0, sticky = W)
input.get()
input.bind("<Return>", main)
##Button
#button1 = Button(root, width = 10, text = "Run", command = main)
#button1.grid(row = 1, column = 0, sticky = W)
##Output box
output1 = Text(root, width = 100, height = 10, bg = "light grey")
output1.grid(row = 3, column = 0, sticky = W)
output2 = Text(root, width = 50, height = 1, bg = "white")
output2.grid(row = 2, column = 0, sticky = W)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
########################################################################
raise_app(root)
root.mainloop()
When I run the code as is, the input box appears but when I click return, I get an error message:
Exception in Tkinter callback
Traceback (most recent call last):
File "/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "/Users/andrehuman/Desktop/Python/programs/Collatz Conjecture/Collatz_alt3.py", line 43, in main
collatz(num)
NameError: name 'num' is not defined
Exactly the same if I try and link a button to the "main" function.
When I comment the input and buttons out, and enter the number through the terminal, everything works as expected. The list of iteration numbers appear in the text box as it should. (And I can even get a Matplotlib graph to display the data visually in the terminal.) If I can get this problem sorted out I want to try and display (or embed) the Matplotlib graph in the GUI.
Anyway, that's it. Any help will be greatly appreciated.
Andre Human
name 'num' is not defined occurs because you're calling collatz(num), but the program does not understand what value you are referring to when you say num. You should assign a value to that name before using it. I assume you want the value to be the contents of your input box.
def main(event):
num = int(input.get())
collatz(num)
You will also need to copy your output1.insert and output2.insert lines to the inside of main. Right now, those lines execute before the window even appears to the user, so there's no way that they can enter a number fast enough to get collatz to trigger before the text gets written. And changing lst after the fact does nothing to the text, since it's not smart enough to notice that the list has changed.
def main(event):
num = int(input.get())
collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
Another problem is that successive calls to collatz will cause lst to grow and grow, because the contents of the list from previous calls is still present. Try entering 4 into the text box, and press Enter a few times. The output will go from 2 to 4 to 6... That's not right.
This is something of a natural hazard when using mutable global state. One possible solution is to reset lst at the beginning of each collatz call.
def collatz(num):
lst.clear()
#rest of function goes here
... But I'm more inclined to make lst local to the function, and return it at the end.
def collatz(num):
lst = []
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
return lst
def main(event):
num = int(input.get())
lst = collatz(num)
#delete previous contents of text boxes
output1.delete(1.0, END)
output2.delete(1.0, END)
output1.insert(END, lst)
output2.insert(END, "Number of iterations are: " + str(len(lst)))
#later, just before mainloop is called...
#lst doesn't exist in this scope, so just set the text to a literal value
output1.insert(END, "[]")
output2.insert(END, "Number of iterations are: 0")
I am trying to create a program that take the user input but rather than displaying the actual input I would like to replace the input with an *
I have tried using this code but I keep getting the error below, I would appreciate any guidance or help.
import msvcrt
import sys
def userinput(prompt='>'):
write = sys.stdout.write
for x in prompt:
msvcrt.putch(x)
entry = ""
while 1:
x = msvcrt.getch()
print(repr(x))
if x == '\r' or x == '\n':
break
if x == '\b':
entry = entry[:-1]
else:
write('*')
entry = entry + x
return entry
userEntry = userinput()
Error:
Traceback (most recent call last):
File "C:\Users\Mehdi\Documents\Teaching\KS5\AS CS\getPass.py", line 24, in <module>
userEntry = userinput()
File "C:\Users\Mehdi\Documents\Teaching\KS5\AS CS\getPass.py", line 9, in userinput
msvcrt.putch(x)
TypeError: putch() argument must be a byte string of length 1, not str
You could use the Tkinter module to get the user input.
here's the code
from tkinter import *
root = Tk()
entry = Entry(root)
entry.pack()
entry.config(show='*')
userinput = entry.get()
You can replace the '*' in the config function with any symbol you'd like. That symbol will replace the answer. The value that the user inputs is stored in the entry.get() function which you should save as a variable.
Put a print statement before this so that they know what you want them to put in the entry. Or you can just do this before the entry
label = Label(root, text='Input the text here')
label.pack()
As per the error you get, putch gets a byte, not a string, so use
for x in prompt:
msvcrt.putch(x.encode()[:1])
(the [:1] is usually not necessary, just to make sure the bytes array is of length 1 as required)
More common practice than using streams would be to use msvcrt.getch and loop until you get a newline, while printing a string of the user input length full of * every time and printing to the same line by carriage return at the end of the printing function:
import msvcrt
def getch():
return chr(msvcrt.getch()[0])
def hidden_input (input_message = 'enter input:'):
user_input = ''
new_ch = ''
while new_ch != '\r':
print(input_message, '*' * len(user_input), ' ' * 20, end = '\r')
user_input = user_input[:-1] if new_ch == '\b' else user_input + new_ch
new_ch = getch()
return user_input
hidden_input()
Trying to get used to the tkinter gui but I'm running into a problem with setting up an input box. I wanted to make a simple number guessing program using Entry to input an integer and a button to submit the guess. I'm getting an str to int conversion error when I use int(GuessBox.get()) and I'm not sure what to do.
ValueError: invalid literal for int() with base 10: ''
from tkinter import *
import random
def makeAGuess():
guess = int(GuessBox.get())
print(guess)
if guess == Answer:
print("you got it!")
return False
elif guess > Answer:
print("Too High, try again")
return True
else :
print("Too low, try again")
return True
Answer = random.randint(1, 100)
main = Tk()
label = Label(main, text = "Guess a number")
label.pack()
GuessBox = Entry(master = main)
GuessBox.pack()
submitGuess = Button(master = main, text = "Submit Guess", command = makeAGuess())
submitGuess.pack()
main.mainloop()
You need to pass the function as an object, don't call it.
submitGuess = Button( master = main, text = "Submit Guess", command = makeAGuess )
Otherwise makeAGuess is called when the Button is created, but not passed any arguments.
With this change your code works perfectly for me.