I'm having trouble with an IF statement resulting from when a button is clicked.
The code I have so far is below, basically what I need is once the button is pressed it asks the question 'Are you sure you want to update?' This works fine. The user then clicks yes or no. No closes the pop up box (working ok), if the user clicks yes then it checks to see if the entry is blank. If it is it keeps the original variable (working ok), it also checks to see if the entry is a float and if it isn't then return an error message, this brings back an error message even if it is a float, what I want it to do is if its a float then use the entered value which the else statement should do. but it keeps bringing back the messagebox error.
def updatetimings():
ask = messagebox.askquestion('Validation','Are you sure you want to update timings?')
if ask =='yes':
try:
a = newv5c.get()
if a == "":
e1 = v5c_timing
elif type(a) != float :
messagebox.showinfo('Error','Please enter decimal numbers only')
else:
e1 = a
except ValueError:
messagebox.showinfo('Error','Please enter decimal numbers only')
pass
Maybe Psuedocode may help:
BUTTON CLICKED:
QUESTION ASKED
NO CLOSES WINDOW
YES = IF ENTRY IS BLANK THEN USE OLD VARIABLE
OR ENTRY = FLOAT THEN USE NEW VARIABLE
IF ITS ANY OTHER TYPE THEN SHOW ERROR MESSAGEBOX ,'WRONG TYPE'
I've set the entry as a StringVar() if that is the problem.
Thanks
In their comment, hiro protagonist suggested that a might always be a str. I agree that this is probably the case (although I don't know for sure). This is one way to structure your code to use float() to parse a value out of a:
def updatetimings():
ask = messagebox.askquestion('Validation','Are you sure you want to update timings?')
if ask == 'yes':
a = newv5c.get()
if a == '':
e1 = v5c_timing
else:
try:
# Try parsing a as a float. If it works, store the result in e1.
e1 = float(a)
except ValueError:
# If we're not able to parse a as a float, then show an error message.
messagebox.showinfo('Error','Please enter decimal numbers only')
I hope this helps!
Related
I'm pretty new to Python, Lately I was working on user input validation with nested try/except. My code is as below:
def user_input():
try:
myinput = 0
myinput = float(input("Enter a numberic value:"))
if myinput > 10:
raise ValueError('Expect a number between 0 and 10')
#print(myinput)
return myinput
except:
print("something went wrong try again!")
user_input()
print(user_input())
Though it was almost working as expected, I noticed an issue. When I call this function and at the initial stage if I pass an acceptable input the function returns the correct numeric value.
But when I try to input a false value initially and subsequently passing the correct value, the function seems not returning the numeric value. I tried to print the return output inside the function, there I'm getting the expected value.
Thanks in Advance
Try return user_input() instead of user_input() in the except block.
I am using pyinputplus and specifically inputNum
https://pyinputplus.readthedocs.io/en/latest/
This is what my code looks like:
msg = 'Enter value to add/replace or s to skip field or q to quit: '
answer = pyip.inputNum(prompt=msg, allowRegexes=r'^[qQsS]$', blank=False)
My goal is to allow any number but also allow one of the following q,Q,s,S.
However when I run the code and enter 'sam' the code crashes because later on I am trying to convert to float(answer).
My expectation is that allowRegexes will not allow this and will show me the prompt again to re-enter.
Please advise!
It seems pyip.inputNum stops validating the input if you provide allowRegexes argument. See the allowRegexes doesn't seem work properly Github issue.
You can use the inputCustom method with a custom validation method:
import pyinputplus as pyip
import ast
def is_numeric(x):
try:
number = ast.literal_eval(x)
except:
return False
return isinstance(number, float) or isinstance(number, int)
def raiseIfInvalid(text):
if not is_numeric(text) and not text.casefold() in ['s', 'q']:
raise Exception('Input must be numeric or "q"/"s".')
msg = 'Enter value to add/replace or s to skip field or q to quit: '
answer = pyip.inputCustom(raiseIfInvalid, prompt=msg)
So, if the text is not and int or float and not equal to s/S/q/Q, the prompt will repeat showing up.
I have a function that asks a user for confirmation via a prompt. It accepts y or n as answers, otherwise it asks again.
Now, I want to write a unittest for this function. I can test the correct behaviour for y or n just fine, but how do I test that my function correctly rejects inacceptable input?
Here's the code for foo.py:
def get_input(text):
"""gets console input and returns it; needed for mocking during unittest
"""
return input(text)
def confirm(message='Confirm?', default=False):
"""prompts for yes or no response from the user. Returns True for yes and
False for no.
'default' should be set to the default value assumed by the caller when
user simply types ENTER, and is marked in the prompt with square brackets.
"""
if default:
message = '%s [y]|n: ' % (message) # default answer = yes
else:
message = '%s y|[n]: ' % (message) # default answer = no
while True:
answer = get_input(message).lower()
if not answer:
return default
if answer not in ['y', 'n']:
print('Please enter y or n!')
continue
if answer == "y":
return True
if answer == 'n':
return False
answer = confirm()
print(answer)
And here is my Test class:
import unittest
import foo
class TestFoo_confirm(unittest.TestCase):
"""testing confirm function
"""
#unittest.mock.patch('foo.get_input', return_value='y')
def test_answer_yes(self, _):
self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered
So, how do I write a similar test for an input-value like '1' (or how do I need to adjust my confirm() function to make it testeable)?
Currently, if I call foo.confirm() from the unittest file, it just gets stuck in an infinite loop and it doesn't return anything. (I understand why this is happening, just not how to circumvent it.)
Any ideas?
You could try this:
import unittest, unittest.mock
import foo
class TestFoo_confirm(unittest.TestCase):
"""testing confirm function
"""
#unittest.mock.patch('foo.get_input', return_value='y')
def test_answer_yes(self, _):
self.assertEqual(foo.confirm(), True) # confirmed if 'y' was entered
#unittest.mock.patch('builtins.print')
#unittest.mock.patch('foo.get_input', side_effect=['1','yn','yes','y']) # this will make the mock return '1', 'yn' and so on in sequence
def test_invalid_answer(self, mock_input, mock_print):
self.assertEqual(foo.confirm(), True) # it should eventually return True
self.assertEqual(mock_input.call_count, 4) # input should be called four times
mock_print.assert_called_with('Please enter y or n!')
In the second test case, we imitate a user who enters three invalid inputs, and, after being prompted again, finally enters 'y'. So we patch foo.get_input in such a way that it returns 1 the first time it's called, then yn, then yes and finally y. The first three examples should cause the confirm function to prompt the user again. I also patched the print function, so that the 'Please enter y or n!' message wouldn't show up when testing. This isn't necessary.
Then we assert that our mock input was called four times, meaning that the first three times, the confirm function reprompted.
Finally we assert that the print function was called (at least once) with 'Please enter y or n!'.
This does not test if the correct number of print statements were made or if they were in correct order, but I suspect this would be possible too
import tkinter
window = tkinter.Tk()
def abc(event):
ans=0
numberss=['7','8','9']
omenu2['menu'].delete(0, 'end')
for number in numberss:
omenu2['menu'].add_command(label=numberss[ans], command=efg)
ans=ans+1
def efg(event=None):
print('yee')
numbers = ['1','2', '3']
number=['4','5','6']
var = tkinter.StringVar(window)
var1 = tkinter.StringVar(window)
omenu = tkinter.OptionMenu(window, var, *numbers, command = abc)
omenu.grid(row=1)
omenu2 = tkinter.OptionMenu(window, var1, *number, command = efg)
omenu2.grid(row=2)
after you have entered the first option menu, it will update the second one. when you enter data into the second one, it runs the command, but doesn't show you what you entered. i do not want to include a button, and i know that the command works and not on the second
i found some code that changed the options of the second menu, however when i ran this, the command wouldn't work as it was changed to tkinter.setit (i would also like to know what is does. i do not currently understand it)
omenu2['menu'].add_command(label=numberss[ans], command=tkinter._setit(var1, number))
this has been taken from a larger piece of code, and has thrown the same error
You should set your StringVar(var1) new value.
def abc(event):
numberss=['7','8','9']
omenu2['menu'].delete(0, 'end')
for number in numberss:
omenu2['menu'].add_command(label=number, command=lambda val=number: efg(val))
def efg(val, event=None):
print('yee')
var1.set(val)
You are using for loop so you don't need ans(at least not in this code) since it iterates over items themselves.
I am writing a code for the wind chill index for an assignment for college.
The prof wants us to prevent the code from crashing upon user input of blank or letters, without using Try/Except Blocks. (He refers to string methods only).
Instead of crashing it should out put an error message eg. ("invalid input, digits only")
I tried utilizing the string.isdigit and string.isnumeric, but it won't accept the negative degrees as integers.
Any suggestions? (code below)
Would another "if" statement work?
Replace the punctuation:
if temperature.replace('-','').replace('.','').isnumeric():
Use an infinite loop and check that everything seems right.
Whenever there's an error, use the continue statement. And when all checks and conversions are done, use the break statement.
import re
import sys
while True:
print("Temperature")
s = sys.stdin.readline().rstrip('\n')
if re.match("^-?[0-9]+(\\.[0-9]+)?$", s) is None:
print("Doesn't look like a number")
continue
if '.' in s:
print("Number will be truncated to integer.")
temp = int(float(s))
if not (-50 <= temp <= 5):
print("Out of range")
continue
break