How to capture keypress event in Tkinter? - python

I have a spinbox that has a values ranges from 0-366, it means it only allowed a numeric data type and a backspace. Whenever a user type a character, it automatically deleted if it not a number type. I'm from a C# background and this is my first attempt in Python language. Here is my code.
def validate(event):
charPress=event.keysym
val=sbDays.get() #previous values
if not charPress.isdigit():
sbDays.config(textvariable=StringVar(windows).set(val))
sbDays=tk.Spinbox(frame,from_=0,to=366,borderwidth=sbBorderWidth)
sbDays.place(relx=initialX,rely=yDistance,relwidth=sbWidth)
sbDays.config(validate='all',validatecommand=(windows.register(validate),'% P'))
sbDays.update()
sbDays.bind('<Key>',validate)
From the code above, when I run it, it returns nothing. Since I'm from C# background. This is what I actually need. This is the C# keypress event
public static void TextBox_KeyPress_NumberBackspace(object sender, KeyPressEventArgs e)
{
char keyChar = e.KeyChar;
if (char.IsNumber(keyChar) || char.IsControl(keyChar))
e.Handled = false;
else
e.Handled = true;
}
tbDays.KeyPress += TextBox_KeyPress_NumberBackspace;

You're not using the validatecommand attribute properly. The function should never directly change the value. The job of the validatecommand function is to return either True or False. If it returns True the input will be allowed, otherwise the input will be rejected.
If it returns anything else besides True or False (including None) or if the function attempts to change the value, the validation function will be disabled.
You can configure tkinter to send the information you need to determine whether the data is valid or not. You should not be relying on the value returned by get(), since that will only return the previous value rather than the value being input from the user.
Since you're only wanting to allow integers, and you want to limit the value to a max of 366, the best solution would be to have tkinter pass in the value if the edit is allowed (using %P), which you can then use to determine whether or not it is valid.
You also should not bind to the <Key> event, it's unnecessary for the validation to function, and in fact would get in the way of it working.
The function would look something like this:
def validate(new_value):
if new_value == "":
# allow blank entry so user doesn't get frustrated
# when trying to delete the first character
return True
try:
value = int(float(new_value))
except ValueError:
return False
return value >= 0 and value <= 366
You should configure the validatecommand like the following (notice there's no space between % and P):
sbDays.configure(validate='all',validatecommand=(root.register(validate), '%P'))
You will probably need to add a little extra code elsewhere to handle the edge case where the user deletes everything in the widget and doesn't enter anything else. For example, you could use something like value=sbDays.get() or 0.

Related

Why is ActiveTagMatcher is ignoring some of the values I'm passing to it?

I've been successfully using ActiveTagMatcher to tag some tests that should be run under some conditions, but for some reason ActiveTagMatcher is ignoring some of the values I set. I've boiled it down to this example:
features/test.feature:
Feature: foo
# This does not work!
#only.with_variable_one=True
Scenario: variable one must be True
Then something
# This works fine.
#only.with_variable_two=foo
Scenario: variable two must be foo
Then something
# Try to see if shortening it to something that does not use the `=` works.
# It does not work either.
#only.with_variable_one
Scenario: variable two must be foo
Then something
features/environment.py:
import os
from behave.tag_matcher import ActiveTagMatcher
def before_all(context):
condition = context.config.userdata.get("condition", None) is not None
values = {
"variable_one": condition,
"variable_two": "foo"
}
context.active_tag_matcher = ActiveTagMatcher(values)
def before_scenario(context, scenario):
if context.active_tag_matcher.should_exclude_with(scenario.effective_tags):
scenario.skip(reason="Disabled by an active tag")
return
I have a features/steps/something.py which contains just enough to avoid complaints about the step being undefined:
#then(u"something")
def step_impl(context):
pass
Running behave should only skip the 1st scenario only if I do not invoke it with -D condition, but it always skips the 1st scenario! Conversely, it is never skipping the last scenario!
I'm using Behave 1.2.5.
Quick Answer
Make sure all your values in your values dictionary are strings:
values = {
"variable_one": str(condition),
"variable_two": "foo"
}
And #only.with_variable_one for your last scenario cannot work. You must have an equal-sign and a value for the tag to work with ActiveTagMatcher.
Longer Explanation
Let's start with the easy bit. The tag #only.with_variable_one is simply not recognized by the ActiveTagMacher at all. There exist other tags that don't need to use an operator to indicate that they should be active if the condition they correspond to is true. For instance, you write #wip when you want to use Behave's builtin work-in-progress feature, not #wip=True. However, the ActiveTagMacher requires that there be an equal sign in the tag that you use, otherwise it does not recognize it. So #only.with_variable_one is flat out ignored.
The reason #only.with_variable_one=True is not working for you is because ActiveTagMatcher's parsing of the tag will result in reading True as the string "True" whereas in your values dictionary you pass a the boolean value True. ActiveTagMacher uses operator.eq to check for equality. So the test is performs is equivalent to "True" == True, which is false in Python. So the equality check always fails and thus ActiveTagMacher always excludes your scenario. You can fix this by converting your boolean to a string:
values = {
"variable_one": str(condition),
"variable_two": "foo"
}
In fact, every single value in your values dictionary must be a string. For instance, if you have a number in there, you must also convert it to a string. Otherwise, tests that are performed against that number won't ever be true.

wx: validate() controls in a container stops after first invalid

i am validating user input controls such as textboxes and dropdowns. The problem i have is, that the validation (the containers.Validate()) stops after finding the first invalid control. I expected it to validate all controls, before it returns. I want to display the user all invalid controls at once, not one by one. Is there a way to do so?
(using wx.WS_EX_VALIDATE_RECURSIVELY will propagate to the children, but only if no other control was invalid before)
Edit:
Instead of overwriting Validate() for every control, i decided to have a separate function, which iterates over all children and validates them.
Note: This function will not be called by default OnOK event. To do so, the event handler must be rebound.
def ValidateRecursively(control):
'''Validate this control and its children recursively'''
validator = control.GetValidator()
# no validator -> valid
isValid = validator.Validate(control) if validator else True
for childControl in control.GetChildren():
# only validate enabled controls
if childControl.IsEnabled():
isValid &= ValidateRecursively(childControl)
return isValid
To do this you would need to override Validate() in your dialog and iterate over all controls yourself without stopping at the first invalid one, like the base class implementation does.
Unfortunately you will probably need to duplicate the code here as there is no way to reuse it. On the bright side, you can use it almost as is with just changing the return false lines to remember the error instead and return it at the end.

using boolean in python from inputs

PhoneValue=0
if (condition== "new"):
PhoneValue=int(PhoneValue+10)
else:
PhoneValue=int(PhoneValue+9)
if GPS==bool(input("true")):
PhoneValue=int(PhoneValue+1)
else:
PhoneValue=int(PhoneValue)
if WiFi==eval(bool(input("true"))):
PhoneValue=int(PhoneValue+1)
else:
PhoneValue=int(PhoneValue)
if camera==eval(bool(input("true"))):
PhoneValue=int(PhoneValue+1)
else:
PhoneValue=int(PhoneValue)
global PhoneValue
This is my code. I am supposed to be able to input the condition, GPS, camera, and WiFi and the code evaluates the input and gives points for each condition. If the phone is new it gets ten points and if used it gets nine. For GPS, Camera, and WiFi it wants me to use boolean to either give it a point for true or no points for false. I am wondering how do I convert the input string into boolean in order to add it to phone value?
There's a lot wrong in this code. Firstly, the input() command is defined
input([prompt])
If the prompt argument is present, it is written to standard output without a trailing newline.
which means your call to input("true") prints "true" on the console and waits for a line of input. That's not what you were hoping for.
Your use of eval is bad. Almost every use of eval on user input is a problem. But you saved yourself here by accident: eval(bool(text)) is superfluous. The only thing that bool() can return True or False neither of which is dangerous to eval, but since you already had a boolean in hand, eval'ing it didn't do anything.
Converting the result of integer addition to an int() is useless, and your if / else clauses can be more clearly written as:
if input("Is there a GPS? "):
PhoneValue += 1
with no else clause needed. Unfortunately, this has almost no chance of getting correct input. If I type "True" the if block will trigger. It will also trigger if I write "no", "false", or "JosEduSol", those will be evaluated as True also. The declaration at the end
global PhoneValue
does absolutely nothing as the last line. In fact, you should probably just forget that global exists because most everybody uses it incorrectly.
There are more faults in the code, and you should really get assistance from a teacher or get a better learning resource.

PyQT5: What's this boolean passed when a menu action is triggered?

So I'm relatively new at Python, and I've been trying to learn PyQt. I wanted to create a menu dynamically based on the contents of a list. I found an example which I adapted and it looked like this:
for someText in myList:
entry = QAction(someText,MainWindow)
self.myMenu.addAction(entry)
entry.triggered.connect(lambda menuItem=someText: self.doStuff(menuItem))
entry.setText(someText)
The menu was created but when a menu item was chosen doStuff() always got passed a value of False. So I changed the above to:
for someText in myList:
entry = QAction(someText,MainWindow)
self.myMenu.addAction(entry)
entry.triggered.connect(lambda bVal, menuItem=someText: self.doStuff(bVal,menuItem))
entry.setText(someText)
and sure enough everything now works as I'd like. I still get the False in bVal which I just ignore.
I've tried looking at the PyQt documentation but the reference section links to the C++ documentation and it's not obvious to me from that what's going on.
I'd like to understand what the boolean value is and why, in my case, it's always False. I've tried changing various things but I haven't managed to find a scenario where it's True.
Thanks
PyQT5.4, Python 3.4.2 on Windows.
The C++ documentation for the triggered signal shouldn't be too hard to understand:
void QAction::​triggered(bool checked = false)
...
If the action is checkable, checked is true if the action is checked, or false if the action is unchecked.
So the signal is emitted with a boolean parameter which indicates the action's 'checked' state, and this parameter overwrote the default value for your menuItem argument.

How to use a callback function in python?

I wonder how to correctly use python 2.7 callback functions.
I have some callback functions from Cherrypy auth examples in my code.
(These callbacks return a function that can evaluate to True or False, depending on the logged in user being in a group or not.)
I wonder if a callback is executed or not if I write a piece of code like this:
Given the definition from the library is:
def member_of(groupname):
def check():
if groupname == 'admin':
if cherrypy.request.login == 'joe':
return True
if cherrypy.request.login == 'toni':
return True
return False
return False
# .... (other groups checked in the same way)
return check # returns a callback function from my understanding?
How can I apply and execute the callback in my code?
If I put it like this:
if member_of('admin'):
do_something()
else:
do_something_else()
Will this execute the calllback and check for the admin group? Or will it find out if the value of "member_of" is a function definition and a function definition is probably always a "True" value (or maybe a False value) but both are wrong, because it needs to be executed
Can you enlighten me on this? How can I make sure a callback is executed? An how can I pass it around as it is?
In python, like in many other languages, a variable can also contain a function and you can pass them around like other variables that contain e.g. numbers or strings.
CherryPy's member_of function itself does return a function in your example.
I am explaining it in simple steps:
If you write member_of() it returns the result of the function member_of() which is the function with the name check in this case.
cb_function = member_of('admin')
At this point the variable cb_function holds the result of calling the function member_of, and in the last line member_of returns check, which was defined within the function member_of as another function!
You have to call the first result again, because you can and you have to treat it in almost the same way as a local function, that you defined in the current context, to get the final result, by doing something like:
my_result = cb_function()
And then you would continue and use the result. For example you could check its boolean value:
if my_result:
# do something
...
The 3 steps from above together can be written shorter:
cb_function = member_of('admin')
if cb_function():
# do something
...
Or even shorter:
if member_of('admin')():
# do something
...
At first it may appear a little strange in python to have the double ()(), but if you think about it for a while it makes sense.
If you execute it, it is plain simple.
member_of() will return method object check.
you have to execute to get result by doing something like if member_of('admin')():
or,
k=member_of('admin')
if k():
To do your task.

Categories

Resources