Keybinding in tkinter Python - python

If I type '*' in keyboard, it should be typed as 'x' in the entry field. Below is the sample code. I'm new to tkinter.
from tkinter import *
def func(number):
x = str(e1.get())
e1.delete(0, END)
e1.insert(0, str(x) + str('x'))
main = Tk()
main.geometry('200x50')
e1=Entry()
e1.bind('*',func)
e1.pack()
main.mainloop()
Here I'm getting 'x*'. But I need only 'x' to be typed in the entry field. Any suggestions will be really helpful.

You need to ignore the * character entered by returning 'break' at the end of func(). Also your logic will not work if the input cursor is not at the end of the input string.
Below is a modified func():
def func(event):
# add the "x" at the insertion position
event.widget.insert('insert', 'x')
# ignore the entered "*" character
return 'break'

Related

taking an integer value from an entry widget in tkinter

I wrote some simple code to describe my problem: I want to take an integer value from an entry to use it later.I tried also to use a spin box. here is my code:
from tkinter import*
win=Tk()
win.geometry('300x200')
e=Entry(width=10)
e.pack()
y=int(e.get())
print(y*2)
I always get the same error:
y = int(e.get())
ValueError: invalid literal for int() with base 10 ' '
I don't know why this is happening!
ValueError: invalid literal for int() with base 10 ' '
means that you are trying to convert the string "" to an integer. This, of course, is invalid. The reason you are trying to convert an empty string to an integer is that you are not allowing any value to be placed into the entry.
A good way to allow this to happen would be using a button, which calls a function to get the value within the entry and print it. Also, you are missing the line win.mainloop() which you would need at the end of your code.
Here is the example code you are probably asking for:
from tkinter import *
win = Tk()
win.geometry('300x200')
def printVal():
y = int(e.get())
print(y*2)
e = Entry(width=10)
e.pack()
b = Button(text="push me to print the value in e")
b.pack()
win.mainloop()
This code will still return errors if the value in the entry is not a valid integer, so if you want it to be robust, you'll have to play around with it.
there is several problems in your example, for example. in which moment you are trying to get value from Entry?
your code should looks something like:
from tkinter import *
def validate(value):
print (value)
try:
if value:
return int(value)
except:
return None
def calculate():
x = e1.get()
x_validate = validate(x)
if x_validate == None:
e1.delete(0, END)
e1.insert(0, 'you need to enter integer')
Label1.config(text='')
else:
result = x_validate*2
Label1.config(text=result)
win = Tk()
e1 = Entry(win)
e1.grid(row=0, column=0)
Button(win,text='Calculate', command = calculate).grid(row=0, column=1)
Label(win,text='Result').grid(row=1, column=0)
Label1 = Label(win)
Label1.grid(row=1, column=1)
win.mainloop()
Example if you enter integer and press calculate
Example if you enter string and press calculate

When I highlight my entry(tkinter) and press backspace, nothing happens?

I'm working on a program regarding math equations, so in my program there are tons of entries but I'm having difficulties clearing them.
For the entries I'm restricting both a character limit and I only allow number and (",") or ("."). If I type for example ' 1000 ' in my entry, then highlight it and press backspace everything works out. But as soon as I type ' 100,25 ' and add a comma into the mix. It wont delete after pressing backspace.
from tkinter import *
root = Tk()
def validatecontent(var):
return (var.isdigit() == bool(var)) or var == (",") or var == (".")
vcmd = (root.register(validatecontent), '%S')
def character_limit6(var):
if len(var.get()) > 0:
var.set(var.get()[:6])
var = StringVar()
entry = Entry(root, textvariable=var, validate='all',
validatecommand=vcmd)
entry.pack()
var.trace("w", lambda *args: character_limit6(var))
root.mainloop()
Edit: It seems that the problem is that it refuses to recognize a string of "," or ".", alternatively a string of numbers and "," / "." as legitimate. The following seems to work:
from tkinter import *
root = Tk()
var = StringVar()
var.trace("w", lambda *args: character_limit6(var))
def validatecontent(var):
return var.isdigit() == bool(var) or "," in var or "." in var
def character_limit6(var):
if len(var.get()) > 0:
var.set(var.get()[:6])
vcmd = (root.register(validatecontent), '%S')
entry = Entry(root, textvariable=var, validate='all',
validatecommand=vcmd)
entry.pack()
root.mainloop()
In the question when you delete a selection the var parameter in validatecontent is a string eg. '0,0' which fails your validation routine.
Do you want to accept only valid float strings?
Are you expecting strings like '1,234.45' or '123,45'?
Below I've assumed '123,45'
Validatecontent tries to convert the string to a float after replacing any ',' with '.'.
If it can it returns true otherwise it returns True only if the string is empty.
from tkinter import *
root = Tk()
def validatecontent(var): # Amended var is now the complete string.
try:
temp = var.replace(',', '.') # Replace ','' with '.' for float conversion assumes , is decimal point.
# temp = var.replace(',',"") # Or replace , with empty if , is a thousands delimiter.
fl = float(temp)
# if temp converts to a float return length of string is less than 7
return len(var) < 7
except ValueError:
return var == "" # If it doesn't convert to a float only accept an empty string.
vcmd = (root.register(validatecontent), '%P') # '%P' passes the whole new string to validate content.
entry = Entry(root, validate='key', validatecommand=vcmd) # Validate on key only.
entry.pack()
root.mainloop()
There may well be better validation functions involving regular expressions. This is reasonably easy to follow though. The entry can still be linked to a StringVar if required. It's not required to do the validation though.

How can I have a Tkinter program scan a text box for inputs in real time?

Ultimately I want to make a small program with a text box where you have to type in the first 50 or so digits of Pi. What I want is for nothing to happen if the user types the correct characters, but I want something to flash red if they input the wrong character. For example, if the user types "3.1", nothing happens but the text showing up in the text box, but if they then type the wrong number, like "3.15", I want something to flash red.
from tkinter import *
def input(event):
inp = (ent.get('1.0', END))
if inp == '3':
print(inp)
else:
print(('--') + (inp))
root = Tk()
root.title('pi, okay')
root.geometry('425x50')
ent = Text(root, width = 50, height = 1)
ent.bind('<KeyRelease>', input)
ent.pack()
mainloop()
What I think SHOULD happen with this is for the console to print "3" IF the user inputs a "3", and for the console to print "--(whatever else the user would have typed)" if it is not a 3. But what actually happens is that the program will print "--(input)" no matter what.
You can use something like this if you need only one line of Input:
var = StringVar()
ent = Entry(root, width=50, textvariable=var)
def check_value(var, ent, *args):
pi = "3.1415"
if not pi.startswith(var.get()):
print("wrong input")
ent.config(fg="red")
else:
ent.config(fg="black")
var.trace('w', lambda *args: check_value(var, ent, *args))
ent.pack()
Here, var.trace() will call function check_value everytime when user types anything in Entry widget. You can add you logic there to verify the input value and change UI(or print logs) based on verification result.

Separating Tkinter UI concerns from Logic in Python app

This is my first app ever. It is working well but I would like to separate the UI concerns like getting input and creating labels, from the translation logic. I would then like to remove the output from the previous translation, i.e., only showing one translation on the screen at a time.
How can I separate the translation logic from my Tkinter GUI?
from Tkinter import *
import tkMessageBox
def start():
inputg = input.get()
if len(inputg) >= 2 and inputg.isalpha():
new_word_out = Label(text=(inputg[1:] + (inputg[0] + "ay")).lower().title()).pack()
out_message = Label(text="Cool! Try another!").pack()
# restart()
elif len(inputg) <= 1 and inputg.isalpha():
show_error(message="Whoops! I need 2 or more characters to translate! Try again!")
return
elif len(inputg) >= 1 and not inputg.isalpha():
show_error(message="Whoops! No numbers or symbols please! Try again!")
return
elif len(inputg) == 0:
show_error(message="It seems you haven't given me anything to translate!")
return
def show_error(message):
tkMessageBox.showerror(title="Error", message=message)
return
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
input = StringVar() # stores user input into this variable as a string.
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!").pack()
labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey').pack()
original_entry = Entry(textvariable=input, bd=5, fg='darkgreen').pack()
translate_button = Button(text="Translate", command=start).pack()
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
There are many ways you can separate logic from GUI. generally I would recommend using classes and callback functions. Thus, I made a class that generates the gui. However, the translation is performed by external function called do_translation.
MyFrame does not know much about how do_translation. It only knows it returns translated_str, message and takes string as argument. do_translation does not relay on any gui as well. The do_translation takes only an input string, does what it wants, and returns translated string and message. The MyFrame take this function as a callback. You can make any other translation function, and as long as the input and output are same, it will work.
I rely here on a "Cool" in a massage which indicates that translation was ok. Its poor idea to make it relay on 'Cool' word, but did not want to change your code too much. Probably better to raise some error, or use message codes, etc.
from Tkinter import *
import tkMessageBox
class MyFrame(Frame):
def __init__(self, master, input_callback=None, **kwargs):
Frame.__init__(self, master)
self.set_input_callback(input_callback)
self.create_widgets()
self.pack()
def create_widgets(self):
self.input = StringVar() # stores user input into this variable as a string.
self.labeltitle1 = Label(text="Hello there! This is my Pig Latin Translator!")
self.labeltitle1.pack()
self.labeltitle2 = Label(text="Please enter a word to continue!", fg='darkgreen', bg='grey')
self.labeltitle2.pack()
self.original_entry = Entry(textvariable=self.input, bd=5, fg='darkgreen')
self.original_entry.pack()
self.translate_button = Button(text="Translate", command=self.start)
self.translate_button.pack()
self.new_word_out = Label(text='')
self.out_message = Label(text='')
def set_input_callback(self, some_fun):
self.input_callback = some_fun
def show_error(self, message):
tkMessageBox.showerror(title="Error", message=message)
return
def start(self):
inputg = self.input.get()
if self.input_callback:
translated_str, message = self.input_callback(inputg)
if 'Cool' in message:
self.new_word_out['text'] = translated_str
self.new_word_out.pack()
self.out_message['text'] = message
self.out_message.pack()
else:
self.show_error(message)
def do_translation(inputg):
translated_str = message = ''
if len(inputg) >= 2 and inputg.isalpha():
translated_str = (inputg[1:] + (inputg[0] + "ay")).lower()
message = "Cool! Try another!"
elif len(inputg) <= 1 and inputg.isalpha():
message = "Whoops! I need 2 or more characters to translate! Try again!"
elif len(inputg) >= 1 and not inputg.isalpha():
message = "Whoops! No numbers or symbols please! Try again!"
elif len(inputg) == 0:
message = "It seems you haven't given me anything to translate!"
return translated_str, message
def quit():
ask_exit = tkMessageBox.askyesno(title="Quit", message="Are you sure you want to quit?")
if ask_exit > 0:
root.destroy()
return
root = Tk()
root.title("The Pig Translator")
root.protocol("WM_DELETE_WINDOW", quit)
mf = MyFrame(root)
mf.set_input_callback(do_translation)
root.bind('<Return>', lambda event: start()) # essentially binds 'Return' keyboard event to translate_button
root.mainloop()
Hopefully this will be useful. I know, that there is not too much explanation what is happening here, but, don't have much time to write it. Your problem is very general.

How to get a binded key not to type into Entry box

I have a program in which I want to type in, for example, ĝ when g^ is typed in an Entry box. I have got the ĝ to appear, but seem unable to rid the entry box of the ^ that has been typed in (yes, I have tried to use the delete function). As far as I can work out, the ^ isn't typed out until after the binded functions have occurred, meaning that if I try and index "^" or "/", it hasn't actually been typed out yet.
from tkinter import *
tk = Tk()
entry = Entry(tk)
entry.pack()
entry.bind("^", lambda x: accent(entry, "^"))
entry.bind("/", lambda x: accent(entry, "/"))
def accent(object, accent):
global entry
letter = entry.get()[len(entry.get())-1]
entry.delete((len(entry.get())-1), len(entry.get()))
if accent == "^":
if letter == "a":
entry.insert(END, "â")
if letter == "g":
entry.insert(END, "ĝ")
if accent == "/":
if letter == "a":
entry.insert(END, "á")
Your binding needs to return "break", which tells tkinter to stop any further processing of the event. Returning "break" will prevent the character from being inserted.

Categories

Resources