I have trouble finding a way to limit the entry length of entry widgets, I would like to limit it to 20 characters, i.e. when I click on a sequence or the other I would like to be able to edit it but stay in the 20 characters limit. In or order to keep the code light , should I use a regex , a loop or check the entry with an event ?
Here is my code:
import Tkinter
from Tkinter import *
import tkFileDialog
root = Tkinter.Tk()
edit1 =StringVar()
edit2 =StringVar()
s = StringVar()
s = "GATACACGCGCGCGTATATATTACGCGCGCGATACA"
lb01=Label(root,text="sequence1")
lb01v=Entry(root,textvariable=edit1,width=20)
lb01v.delete(0, END)
lb01v.insert(0, s[6:20])
lb01.grid(sticky=W,row=1,column=1)
lb01v.grid(row=1,column=2)
lb02=Label(root,text="sequence2")
lb02v=Entry(root,textvariable=edit2,width=20)
lb02v.delete(0, END)
lb02v.insert(0, s[0:6])
lb02.grid(sticky=W,row=2,column=1)
lb02v.grid(row=2,column=2)
root.mainloop()
Ok I did try with the trace variable, on a short piece of test code , this is excactly what I was searching for !! I like the fact you can prototype so easily in Python ;)
def main():
pass
if __name__ == '__main__':
main()
from Tkinter import *
def callback(sv):
c = sv.get()[0:9]
print "c=" , c
sv.set(c)
root = Tk()
sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
e = Entry(root, textvariable=sv)
e.pack()
root.mainloop()
I know its too late to add any answers to this, just found a simpler way to represent what Wabara had answered. This will help if you need multiple entry limits and each to a user-defined length limit. Here's a code working on Python 3.6.5:
def main():
pass
if __name__ == '__main__':
main()
from tkinter import *
def limit_entry(str_var,length):
def callback(str_var):
c = str_var.get()[0:length]
str_var.set(c)
str_var.trace("w", lambda name, index, mode, str_var=str_var: callback(str_var))
root = Tk()
abc = StringVar()
xyz = StringVar()
limit_entry(abc,3)
limit_entry(xyz,5)
e1 = Entry(root, textvariable=abc)
e2 = Entry(root, textvariable=xyz)
e1.pack()
e2.pack()
root.mainloop()
The simplest solution is to put a trace on the variable. When the trace fires, check the length of the value and then delete any characters that exceed the limit.
If you don't like that solution, Tkinter also has built-in facilities to do input validation on entry widgets. This is a somewhat under-documented feature of Tkinter. For an example, see my answer to the question Python/Tkinter: Interactively validating Entry widget content
I will start off by making an alphabet to measure from. The alphabet is a string and has 26 letters meaning its too long for our use. we want 20 letters only, so our output should be "A" thru "T" only.
I would define a function to make it happen and dump each string thru it that I would want cut to 20 characters or less.
I am making the below code in such a way that it takes as an input any string that is called it takes that input in and processes it to 20 characters in length only...
def twenty(z):
a = z[0:20]
return a
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
so to execute our newly made code, we need only call the print twenty command with the variable we want cut to 20 characters in the parenthesis.
print twenty(alphabet)
-----------------------------------------------------------------
OUTPUT:
ABCDEFGHIJKLMNOPQRST
So you see, it worked, we input the entire alphabet into the program and it cut the string down to 20 letters only. now every time in your code you want to cut text down to 20 letters, just run the command
twenty(variable)
and it will make sure you have no more letters than that.
Explanation:
def twenty is to define a function with one input that you can call on over and over simply by typing twenty(variable)
the next line is a = z[0:20] Meaning call variable "a" to equal the input from position 0 to position 20 and dont worry about anything past that.
return command is how you get an output from the def function. anytime you create a def function, you should end it with a line.
Related
I need to get only the latest input from my text widget, and then append that character to a list.
I am using
Text.get(1.0,'end-1c')
, and it does not work because the loop constantly gets all the input, instead of only getting the latest input when there is a new latest input.
def main_screen():
start_time=time.time()
tk=Tk()
tk.title('Typing Test')
tk.geometry('800x500')
main_title=Label(tk,text='1 Minute Test',font=('Times New Roman',36))
main_title.pack(pady=5)
directions=Label(tk,text='Start Typing',font=('Times New Roman',14))
directions.pack()
base_text=Label(tk,text=randomizer(),bg='#E0E0EE',font=('Arial',14),wraplength=700,justify=LEFT)
base_text.pack(pady=10)
text_area=Text(tk,font=('Arial',14),width=63,height=7,wrap='word')
text_area.pack()
tk.update()
#WPM Calculation
target_text=randomizer()
typed_text=[]
wpm=0
errors=0
while True:
tk.update()
time_elapsed=max(time.time()-start_time,1)
wpm=round((len(typed_text)/60)/5)
if time_elapsed>=60:
break
#Problem Section
key=text_area.get(1.0,'end-1c')
typed_text.append(key)
for x in typed_text:
if x != target_text:
errors += 1
Alternatively, I tried using a variable in place of the 1.0 in .get, that would increase by one with each iteration of the loop. Next, I tried a try/except command, and put the #Problem Section into a function. I tried calling that function by binding the text area to
'<Key>'
'<KeyPress>'
'<KeyRelease>'
None of these attempts work. I used a print statement to see what those variables are with each iteration of the loop, and using the first method, it just keeps making a longer and longer string that repeats constantly, instead of updating with each new character. Trying the other ways I just got nothing, no output, but no error either. I am completely stuck, and don't know what else to try.
You can bind the text_area with a <KeyPress> event, but you need to pass the list typed_text as an argument so you can append the presses.
So you should do something like this:
text_area.bind("<KeyPress>", lambda _: getKey(_, typed_text))
while True:
tk.update()
time_elapsed = max(time.time() - start_time, 1)
wpm = round((len(typed_text) / 60) / 5)
if time_elapsed >= 60:
break
# Problem Section
for x in typed_text:
if x != target_text:
errors += 1
def getKey(event, list):
list.append(event.char)
print(list)
The text widget supports something called a "mark", which is like a bookmark. You can put a mark anywhere in the text and use it just like a normal index.
Assuming that data is only ever appended to the end of the widget, the simplest solution is to fetch a block of data and then move the mark to the end of the text that you fetched. The next time you fetch data, start at that mark.
Marks have something called "gravity" that defines which character the mark sticks to. For example, if the gravity is "left" and you set it to character "2.2", the mark will always stay adjacent to the character at index "2.2". If the gravity is "right", it will be stuck at the character following index "2.2" (eg: "2.3" or "3.0")
Here's a contrived example that will print only the latest additions to a text widget every five seconds, by tracking the last position that was used to fetch the data.
import tkinter as tk
def get_new_text():
data = text.get("last", "end-1c")
print(f"new data: >>>{data}<<<")
text.mark_set("last", "end-1c")
root.after(5000, get_new_text)
root = tk.Tk()
text = tk.Text(root, wrap="word")
text.pack(fill="both", expand=True)
text.mark_set("last", "1.0")
text.mark_gravity("last", "left")
root.after(5000, get_new_text)
root.mainloop()
Consider the code below:
from tkinter import *
screen = Tk()
e =Entry()
e.pack()
screen.mainloop()
Now how to get to display the length of the characters entered in the e entry widget in real-time? It doesn't matter if the data is displayed in the GUI or Corresponding terminal
There are atleast 3 ways to do this here with one being better than the other:
Using trace from StringVar:
def func(*args):
print(len(var.get()))
var = StringVar()
e = Entry(screen,textvariable=var)
e.pack()
var.trace('w',func)
Every time the value of var is changed, func will be called.
Using bind to each key release:
def func(*args):
print(len(e.get()))
e.bind('<KeyRelease>',func)
Using after(ms,func) to keep repeating the function:
def func():
print(len(e.get()))
screen.after(500,func)
func()
As you can see, the first method is more efficient as it does not unnecessarily prints out values when you select all the items(with Ctrl+A) and so on. Using after() will be the most ridiculous method as it will keep printing the length always as there are no restrictions provided.
Currently, I am working on a Tkinter GUI. In this GUI, I would like to include an Entry field where users can only enter numbers between two boundaries (for instance, a number between 0.0 and 200.0). I have looked into the validatecommand option, but this still does not provide me a solution. Is there somebody who can help me to create a Tkinter Entry with boundaries?
Thank you in advance.
You can get the value by .get() method and then write an if statement to generate an error or reset the value by .set() method when limits are exceeded.
tkinter Entry validation allows to validate characters typed in the Entry field.
I do not know if the validation process built in tkinter allows to validate the entire entered value for being within a given range; however, you can use a common if-else test for that:
In the following example, the values of each entered character in the entry is validated for being a digit. then, upon retrieving the value entry.get, the value is checked to ascertain it is within the desired range, and printed in the console; if not, a ValueError is raised.
the line entry._get, entry.get = entry.get, _get_only_in_range makes a private copy of entry.get, then monkey patches it to inject the desired behavior
A better way is probably to write a class to encapsulate the behavior specific to your entry.
import tkinter as tk
def only_numbers(char): # validates each character as it is entered in the entry
if char.isdigit():
return True
else:
root.bell()
return False
def _get_only_in_range():
num = int(entry._get()) # uses the private copy of the original entry.get
if 0 <= num < 200:
return num
else:
raise ValueError(f'value {num} must be between 0 and 200')
if __name__ == "__main__":
root = tk.Tk()
validation = root.register(only_numbers)
entry = tk.Entry(root, validate="key", validatecommand=(validation, '%S'))
entry._get = entry.get # make private copy of original get
entry.get = _get_only_in_range # calling entry.get now calls _get_only_in_range to validate the value
entry.pack()
tk.Button(root, text='get value', command=lambda: print(entry.get())).pack() # here get points to _get_only_in_range
root.mainloop()
I am creating a search function in my tkinter program and I have a problem with empty widgets getting packed if the ifstatement where the widgets should get packed in is false.
I try to explain what I am trying to do:
I have a while loop which is looping through all files in a folder.
In the while loop is a if statement which checks if the filename contains the searched letters.
If yes, a Label with the filename will be packed. If no, nothing should happen.
But when the filename doesn´t contain the searched letters, a empty widget is been packed.
Here is a short example of my code:
whileLoop = True
search = Entry(root)
fileNameList = ["abc","def","ghi"]
whileLoopCounter = 0
while whileLoop == True:
if search in fileNameList[whileLoopCounter]:
Label(root, text=fileNameList[whileLoopCounter])
whileLoop = False
whileLoopCounter += 1
This is a very short version of my code, because the real code would be way to long. But I hope you understand what I am trying to do.
Thanks for your help.
search = Entry(root)
fileNameList = ["abc","def","ghi"]
for d in fileNameList:
if d in search.get():
Label(root, text=d)
try this! it uses "any". Also you need to do "in" on 'each' string not
on the whole list!
from Tkinter import *
top=Tk()
First Value A that user will input
A = Entry(top)
A.grid(row=1, column=1)
Second value B that user also inputs
B = Entry(top)
B.grid(row=1, column=2)
Calculation - Now I want to add those values (Preferably values with decimal points)
A1=float(A.get())
B1=float(B.get())
C1=A1+B1
Result - I want python to calculate result and show it to user when I input the first two values
C = Label(textvariable=C1)
C.grid(row=1, column=3)
top.mainloop()
First off, welcome to StackOverflow and nice job- your code does (mostly) everything you want it to do! The timing is just off- you create your objects and get the value, but the value hasn't been input by the user yet.
To solve that, you need to put your .get()'s in a function, and you should be using an actual text-variable that you set() after each one (if you just use C1=(float), you'll end up making new floats so the Label isn't pointing to the right one).
(setup... )
B.grid(...)
C1 = Tkinter.StringVar()
C = Label(textvariable=C1) # Using a StringVar will allow this to automagically update
def setC():
A1=float(A.get())
B1=float(B.get())
C1.set(str(A1+B1))
Additionally, you need to set this function so it goes off more than just "immediately on running the program". The simple way to do this is to just have the function call itself .after() some time (in milliseconds).
def setC():
# Body above
top.after(1000, setC) # Call setC after 1 second, so it keeps getting called.
setC() # You have to call it manually once, and then it repeats.
The slightly more advanced and efficient way to update involves events and bindings (binding setC() to fire every time A1 or B1 is changed), but the writeup on that is long so I'll give you that tip and send you to some documentation on that. (Effbot is good tkinter documentation regardless)