How to create a password entry field using Tkinter - python

I am trying to code a login window using Tkinter but I'm not able to hide the password text in asterisk format. This means the password entry is plain text, which has to be avoided. Any idea how to do it?

A quick google search yielded this
widget = Entry(parent, show="*", width=15)
where widget is the text field, parent is the parent widget (a window, a frame, whatever), show is the character to echo (that is the character shown in the Entry) and width is the widget's width.

If you don't want to create a brand new Entry widget, you can do this:
myEntry.config(show="*");
To make it back to normal again, do this:
myEntry.config(show="");
I discovered this by examining the previous answer, and using the help function in the Python interpreter (e.g. help(tkinter.Entry) after importing (from scanning the documentation there). I admit I just guessed to figure out how to make it normal again.

widget_name = Entry(parent,show="*")
You can also use a bullet symbol:
bullet = "\u2022" #specifies bullet character
widget_name = Entry(parent,show=bullet)#shows the character bullet

Here's a small, extremely simple demo app hiding and fetching the password using Tkinter.
#Python 3.4 (For 2.7 change tkinter to Tkinter)
from tkinter import *
def show():
p = password.get() #get password from entry
print(p)
app = Tk()
password = StringVar() #Password variable
passEntry = Entry(app, textvariable=password, show='*')
submit = Button(app, text='Show Console',command=show)
passEntry.pack()
submit.pack()
app.mainloop()
Hope that helps!

I was looking for this possibility myself. But the immediate "hiding" of the entry did not satisfy me. The solution I found in the modification of a tk.Entry, whereby the delayed hiding of the input is possible:
Basically the input with delay is deleted and replaced
def hide(index: int, lchar: int):
i = self.index(INSERT)
for j in range(lchar):
self._delete(index + j, index + 1 + j)
self._insert(index + j, self.show)
self.icursor(i)
and the keystrokes are written into a separate variable.
def _char(self, event) -> str:
def del_mkey():
i = self.index(INSERT)
self._delete(i - 1, i)
if event.keysym in ('Delete', 'BackSpace'):
return ""
elif event.keysym == "Multi_key" and len(event.char) == 2: # windows stuff
if event.char[0] == event.char[1]:
self.after(10, del_mkey)
return event.char[0]
return event.char
elif event.char != '\\' and '\\' in f"{event.char=}":
return ""
elif event.num in (1, 2, 3):
return ""
elif event.state in self._states:
return event.char
return ""
Look for PassEntry.py if this method suits you.

Related

Constantly checking a text box in tkinter

I want a Text label in tkinter to constantly check if its worth some value. I would like it to be in a while loop and exit it when the values match.
My code doesn't work.
while user_input != str(ans):
master = Tk()
master.title("Math")
master.geometry('400x400')
eq = generate_equation(stage=current_stage)
ans = calc(eq)
Label(master=master,text=f"score: {score}").grid(row=0,column=2,sticky=W)
Label(master=master, text=f"{q_num}: {eq}").grid(row=0,column=0 ,sticky=W)
inputtxt = tkinter.Text(master=master,height = 5, width = 20)
inputtxt.grid()
user_input =str(inputtxt.get(1.0,"end-1c"))
mainloop()
Try this:
import tkinter as tk
def check(event:tk.Event=None) -> None:
if text.get("0.0", "end").strip() == "answer":
# You can change this to something else:
text.insert("end", "\n\nCorrect")
text.config(state="disabled", bg="grey70")
root = tk.Tk()
text = tk.Text(root)
# Each time the user releases a key call `check`
text.bind("<KeyRelease>", check)
text.pack()
root.mainloop()
It binds to each KeyRelease and checks if the text in the text box is equal to "answer". If it is, it displays "Correct" and locks the text box, but you can change that to anything you like.
Please note that this is the simplest answer and doesn't account for things like your code adding things to the text box. For that you will need more sophisticated code like this.

Making multiple selections in tkinter

Is there any way to make multiple selections in tkinter?
Here's the code:
from tkinter import *
root = Tk()
text = Text(root , width = 65 , height = 20 , font = "consolas 14")
text.pack()
text.insert('1.0' , "This is the first line.\nThis is the second line.\nThis is the third line.")
mainloop()
Here, I want be able to select multiple text from where ever I want.
Here is an Image(GIF) that explains what I mean:
Is there any way to achieve this in tkinter?
It would be great if anyone could help me out.
I made a short demo, with Control key hold you could select multiple text. Check this:
import tkinter as tk
class SelectableText(tk.Text):
def __init__(self, master, **kwarg):
super().__init__(master, **kwarg)
self.down_ind = ''
self.up_ind = ''
self.bind("<Control-Button-1>", self.mouse_down)
self.bind("<B1-Motion>", self.mouse_drag)
self.bind("<ButtonRelease-1>", self.mouse_up)
self.bind("<BackSpace>", self.delete_)
def mouse_down(self, event):
self.down_ind = self.index(f"#{event.x},{event.y}")
def mouse_drag(self, event):
self.up_ind = self.index(f"#{event.x},{event.y}")
if self.down_ind and self.down_ind != self.up_ind:
self.tag_add(tk.SEL, self.down_ind, self.up_ind)
self.tag_add(tk.SEL, self.up_ind, self.down_ind)
def mouse_up(self, event):
self.down_ind = ''
self.up_ind = ''
def delete_(self, event):
selected = self.tag_ranges(tk.SEL)
if len(selected) > 2:
not_deleting = ''
for i in range(1, len(selected) - 1):
if i % 2 == 0:
not_deleting += self.get(selected[i-1].string, selected[i].string)
self.delete(selected[0].string, selected[-1].string)
self.insert(selected[0].string, not_deleting)
return "break"
root = tk.Tk()
text = SelectableText(root, width=50, height=10)
text.grid()
text.insert('end', "This is the first line.\nThis is the second line.\nThis is the third line.")
root.mainloop()
So I was trying to delete each selection with the Text.delete(index1, index2) but when the first selection in one line is deleted, the indices changes, making the subsequent delete deleting indices not selected (or out of range in the particular line.
I had to work around another way - first deleting from the first selected to the last selected, just like what BackSpace would do by default, then put back every unselected part in the middle. The Text.tag_ranges gives you a list of ranges selected in this way:
[start1, end1, start2, end2, ...]
where each entry is a <textindex object> with a string property (the index). So you can extract the text between end1 and start2, between end2 and start3, etc. to the end, and store these into a variable (not_deleting) so you can insert them back into the text.
There should be better and neater solutions but for now this is what it is... Hope it helps.
For the delete_ method proposed by Qiaoxuan Zhang, I advise to use the Text.tag_nextrange method in a loop like this :
def delete_sel(self):
while 1:
result = self.tag_nextrange(tk.SEL, 1.0)
if result :
self.delete(result[0] , result[1])
else :
break
For those who would like to add the missing features:
correction of selection when changing direction of sliding
take into account the double-click
Take a look here : French site
Short answer: set the exportselection attribute of each Text widget to False
Tkinter gives you control over this behaviour with the exportselection configuration option for the Text widget as well as the Entry and Listbox widgets. Setting it to False prevents the export of the selection to the X selection, allowing the widget to retain its selection when a different widget gets focus.
For example:
import tkinter as tk
...
text1 = tk.Text(..., exportselection=False)
text2 = tk.Text(..., exportselection=False)
you can find more info here: http://tcl.tk/man/tcl8.5/TkCmd/options.htm#M-exportselection

Python/Tkinter dynamically update label

I am currently trying to make a GUI to an existing python program using Tkinter. The program gives the user two options from which the user must choose to either accept or decline. Before using Tkinter the options were placed in the terminal and awaited for a raw_input. (y/n). How can I make this so the canvas text updates with the new data and awaits for the users button click?
To make my question more specific: How can I run another programs code while the Tkinter mainloop is running and make these two interact?
Example code below.
from Tkinter import *
root = Tk()
root.resizable(width=False, height=False)
root.geometry('{}x{}'.format(500,550))
root.wm_title("Tkinter test")
BtnFrame = Frame (root)
BtnFrame.pack(side = BOTTOM)
BtnFrame.place(y=450, x=20)
canvas_1 = Canvas(root, width = "200", height ="300")
canvas_2 = Canvas(root, width = "250", height ="300")
canvas_1.pack(side = LEFT)
canvas_2.pack(side = RIGHT)
textfield_1 = canvas_1.create_text(100,50)
textfield_2 = canvas_2.create_text(100,50,)
def update_textfiel_1(text):
global textfield_1
canvas_1.delete(textfield_1)
textfield = canvas.create_text(100,50,text = text)
def update_textfiel_2(text):
global textfield_2
canvas_2.delete(textfield_2)
textfield1 = canvas1.create_text(100,50,text = text)
Accept = Button(BtnFrame, text="Accept", width=25)
Decline = Button(BtnFrame, text="Decline", width=25)
Accept.pack(side = LEFT)
Decline.pack(side = RIGHT)
root.mainloop()
First off you have some inconsistent variable names in your update_textfiel functions, you can greatly simplify it by using .itemconfigure (documentation for methods on canvas widget)
def update_textfiel_1(new_text):
canvas_1.itemconfigure(textfield_1, text=new_text)
def update_textfiel_2(new_text):
canvas_2.itemconfigure(textfield_2, text=new_text)
If I understand correctly you want a way to have a function that will simply wait for the user to press one of the buttons and then return the result, this is very easy with tkMessageBox:
question = """Do you accept {}?
if you say no you will instead get {}"""
#this message can GREATLY be improved
# But I really don't understand what you are using it for...
def user_confirmation(name1, name2):
response = tkMessageBox.askyesno("Accept or Decline",question.format(name1,name2))
print(response)
if response: # == True
return name1
else:
return name2
I have not yet found a way to make a blocking function that works with the window you have currently...

Replacing Buttons linked to input boxes with checkboxes

This is my current code for selecting different options and have them appearing in the box (Minecraft ArmorStand Generator).
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
inputbox.insert(45, NoAI)
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
Button(text="Add NoAI",command=addNOAI,relief = FLAT, bg = "#eF651A", fg = "white", width= 25, height = 2).place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
What I'd like to to do is replace the buttons with tick boxes, to prevent the buttons being pressed multiple times. If they click the button, add the text, if they untick, remove it.. I'm not sure where to start with this so any hint in the right direction would be nice.
I've got a working solution, you can try it below.
from tkinter import *
default = "/summon ArmorStand ~ ~ ~ {CustomNameVisible:1}"
NoAI = ",NoAI:1"
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
root = Tk()
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
inputbox.delete(0, 7) #delete the text
inputbox = Entry()
inputbox.place(x=10,y=10,width=900,height=50)
var = IntVar() #sets up variable for check button
c = Checkbutton(text="Add NoAI", command=addNOAI, variable=var) #defining check button variable and command
c.place(x=10,y=123)
root.title("WIP")
root.wm_state('zoomed')
root.mainloop()
The only problem is at the moment, you are deleting everything in the entry box (more accurately from position 0, to position 7). I assume that there will be multiple check buttons, all adding their own strings to the entry box.
As a solution, I would suggest extracting everything from the entry box, finding the string you want, taking it out, and putting everything back in again. Here's an example.
def addNOAI():
state = var.get()
if state == 1: #if the state is checked
inputbox.insert(45, NoAI) #then add the text
else: #if the state is not check
contents = inputbox.get() #gets all of contents
position = contents.find(NoAI) #finds the first position of desired string to remove
newcontents = contents[:position]+contents[position+7:] #gets string before the word, and after the word, and joins them
inputbox.delete(0, 'end') #clears input box for new entry
inputbox.insert(45, newcontents) #re-inserts the string
Here, when the user unchecks the box, the program finds the starting position of the string within the contents of the inputbox. Because you know how long the string will be (in this case 7), you can remove the string from the current contents of the inputbox, and place this inside a new variable. Now you have a new string, without the one that was unchecked, you can clear the inputbox, and put the new one in.
Hope this helps!

Bind or Command to get return and button to work

I have a simple question about the bind() method and the command argument.
Usually, in a program, you can click on a button related to what you are doing to execute something or simply press the return key.
In the code below, I tried to do the same and it does actually work.
I was just asking myself if the line bttn.bind('<Button-1>', search) isn't a bit strange, as it relates the mouse click inside the button to the function, and not the pressing of the button itself.
At the beginning, I didn't want to include pressing the return key to execute the entry, and I had written bttn = Button(wd, text='Search', bg='Light Green', command=search), but at that point the search function wasn't an event driven function and had no event argument.
As soon as I wanted to include the return key pressing to do the same job, I had (of course) to write the function with (event), and thus use the bind() method for the mouse button as well.
Is this the "best way" to do it ? Or is there a more idiomatic way of doing it ?
Python3/Windows
from tkinter import *
def search(event):
try:
txtFile = open(str(entr.get()), 'r')
except:
entr.delete(0, END)
entr.insert(0, "File can't be found")
else:
x = 0
while 1:
rd = txtFile.readline()
if len(rd)> x:
longest = rd
x = len(rd)
elif rd == '':
break
txtFile.close()
entr.delete(0, END)
entr.insert(0, longest)
#####MAIN#####
wd = Tk()
wd.title('Longest sentence searcher')
entr = Entry(wd, bg='White')
entr.grid(row=0, column=0)
entr.bind('<Return>', search)
bttn = Button(wd, text='Search', bg='Light Green')
bttn.grid(row=1, column =0)
bttn.bind('<Button-1>', search)
wd.mainloop()
The normal way to share a function between a button and a binding is to make the event parameter optional, and to not depend on it. You can do that like this:
def search(event=None):
...
bttn = Button(..., command=search)
...
entr.bind('<Return>', search)
If you omit the command and rely on a bound event, you lose the built-in keyboard accessibility that Tkinter offers (you can tab to the button and press the space bar to click it).

Categories

Resources