Display every keyboard character pressed Python tkinter - python

When I watched ecnerwala videos on competitive programming, I found something interesting in his video.
This running text
ecnerwala running text
Here is the link of the video
https://www.youtube.com/watch?v=ExmrrXi04vI&t=1030s
So I tried to create something like that running text using tkinter.text widget in python.
I create a window, add text object to it, and bind all keyboard keys to the window. I insert those keys into the text object when any key is pressed. I also make sure that even if I press the enter button, I don't insert it as a newline to the text widget but as the word 'Return' instead
Of course, there is a possibility that the length of all characters I insert into the text is longer than the length of the text widget could fit in one sentence. That's why I want to make the text widget to "always" see the characters from index END-(the number of character the widget could fit) until END
Here is my code
from tkinter import *
def handle(event):
text.config(state = NORMAL)
text.insert(END, event.keysym)
text.config(state = DISABLED)
# Things to be fix
text.doSomethingToMakeItBehaveLikeEntryWidget()
window = Tk()
window.geometry('500x500')
text = Text(window, font = ('Arial', 20), height = 1)
entry = Entry(window, font = ('Arial', 20))
text.config(state = DISABLED)
text.pack()
entry.pack()
window.bind('<Key>', handle)
window.mainloop()
Is there any solution for this problem? Even if I have to use another widget instead of text widget to solve it? Thanks for the help

Given that you use only one line in the text widget, you can use an entry in read-only mode instead.
No matter whether you use an entry or a text widget, you can use the method text.xview_moveto(<fraction>) to set which part of the content is visible, <fraction> being the relative portion of the widget's content which is hidden on the left. If you set this fraction to 1, it will ensure that the last character is visible.
from tkinter import *
def handle(event):
text.config(state=NORMAL)
text.insert(END, event.keysym)
text.config(state="readonly")
text.xview_moveto(1)
window = Tk()
window.geometry('500x500')
text = Entry(window, font = ('Arial', 20), state="readonly", readonlybackground="white")
entry = Entry(window, font = ('Arial', 20))
text.config(state = DISABLED)
text.pack()
entry.pack()
window.bind('<Key>', handle)
window.mainloop()

Try this.
from tkinter import *
from tkinter.scrolledtext import ScrolledText
def handle(event):
text.config(state = NORMAL)
text.insert(END, event.keysym)
text.config(state = DISABLED)
t = text.get('1.0','end-1c')
length= len(t)
width =text.winfo_width()
max_char = int((35/500)*width)
if max_char < length:
a = length-max_char
text.config(state = NORMAL)
text.delete(1.0,END)
text.insert(1.0,t[a:])
text.config(state = DISABLED)
window = Tk()
window.geometry('500x500')
text = Text(window, font = ('Arial', 20), height = 1)
entry = Entry(window, font = ('Arial', 20))
text.config(state = DISABLED)
text.pack()
entry.pack()
window.bind('<Key>', handle)
window.mainloop()

Related

Random Names picker with new draws on Each new Button Click

I'm trying to select random names from a list to display on a textbox in a tkinter GUI, with a button and need new draws to display in tkinter script each time I click the button.
Here's my code so far:
from tkinter import *
from random import sample
root = Tk()
root.title('Name Picker')
root.iconbitmap('c:/myguipython/table.ico')
root.geometry("400x400")
def pick():
# 5 entries
entries = ["1st name","2nd name","3rd name","4th name","5th name"]
# convert to a set
entries_set = set(entries)
# convert back to a list
unique_entries = list(entries_set)
number_of_items = 2
rando = sample(unique_entries, number_of_items)
rando1 = sample(unique_entries, number_of_items)
my_text = Text(root, width=40, height=10, font=("Helvetica", 18))
my_text.pack(pady=10)
if len(my_text.get(1.0, END)) != 0:
my_text.delete(1.0, END)
my_text.insert(INSERT, rando + rando1)
my_text.pack(expand= 1, fill= BOTH)
topLabel = Label(root, text="Picked Names", font=("Helvetica", 24))
topLabel.pack(pady=20)
winButton = Button(root,
text="pick names",
font=("Helvetica", 24), command=pick)
winButton.pack(pady=20)
root.mainloop()
I need several subsets of random draws (I'm using rando and rando1 values as example above).
When I click the button the 1st time it does display the names strings as expected, but when I click again nothing changes (the names remain the same).
What I need is for new draws to display each time I click the button.
I thought of clearing the textbox if not empty and populating back when clicking again on empty textbox (with the delete and insert statements above). But that's not working.
After some research I found this snippet:
# Import package and it's modules
from tkinter import *
# text_update function
def text_updation(language):
text.delete(0, END)
text.insert(0, language)
# create root window
root = Tk()
# root window title and dimension
root.title("GeekForGeeks")
# Set geometry (widthxheight)
root.geometry('400x400')
# Entry Box
text = Entry(root, width=30, bg='White')
text.pack(pady=10)
# create buttons
button_dict = {}
words = ["Python", "Java", "R", "JavaScript"]
for lang in words:
# pass each button's text to a function
def action(x = lang):
return text_updation(x)
# create the buttons
button_dict[lang] = Button(root, text = lang,
command = action)
button_dict[lang].pack(pady=10)
# Execute Tkinter
root.mainloop()
It seems it's close to what I need but the dictionary use seems incompatible with my conversion to a set and then back to a list and then the sampling before the displaying in the textbox of my code.
How could I use the given snippet to solve this issue? Or lese if it's not possible with this snippet, what other other solution of insight to look for would you suggest? Your help is appreciated.
In your code you are calling the widget constructor for the Text widget every time you press the button. What you should do is create the widget outside of the function and use the function just to update the widget's contents. There is also quite a bit of erroneous code as well.
For example:
from tkinter import *
from random import sample
root = Tk()
root.title('Name Picker')
root.iconbitmap('c:/myguipython/table.ico')
root.geometry("400x400")
def pick():
entries = ["1st name","2nd name","3rd name","4th name","5th name"]
number_of_items = 2
rando = sample(entries, number_of_items)
rando1 = sample(entries, number_of_items)
my_text.delete(1.0, END)
my_text.insert(INSERT, rando + rando1)
topLabel = Label(root, text="Picked Names", font=("Helvetica", 24))
topLabel.pack(pady=20)
winButton = Button(root, text="pick names", font=("Helvetica", 24), command=pick)
winButton.pack(pady=20)
my_text = Text(root, width=40, height=10, font=("Helvetica", 18))
my_text.pack(pady=10)
root.mainloop()

Set width of entry object in an optimal way the string is fully visible

I have following code:
from tkinter import *
from tkinter import simpledialog
def resize(lbl):
USER_INP = simpledialog.askstring(title="User input",
prompt="Please enter your message here:")
lbl['state'] = 'normal'
lbl.insert(0, f"Text was \'{USER_INP}\'.")
lbl['state'] = 'readonly'
lbl['width'] = 50
top = Tk()
lbl_entry = Entry(top, state="readonly", justify='center', width=10)
lbl_entry.grid(column=0, row=1)
bt_adjust = Button(top, text = "Resize Entry", command=lambda:resize(lbl_entry))
bt_adjust.grid(column=0, row=0)
top.mainloop()
After pressing the button bt_adjust the width of the Entry object is adjusting automatically. Is their a way to set the width dynamically in that way the text is fully displayed on the Entry element but if the width would be a little smaller the text wouldn't be fully displayed anymore?
Does this help? By using len() to adjusting automatically resizing.
After pressing the button bt_adjust the width of the Entry object is
adjusting automatically.
code in line 10:
lbl['width'] = f'{len(USER_INP)}
Screenshot before resizing:
AScreenshot after resizing:

Tkinter Radio Button Controlling If True

I am trying to do a simple program. I am trying to check if radio button 1 is selected show a button and if radio button 2 selected and if there is a button on the screen disappear it. Please help me.
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
bati = tkinter.Tk()
bati.geometry('500x500')
bati.title('Project')
def hello():
messagebox.showinfo("Say Hello", "Hello World")
def askfile():
bati.filename = filedialog.askopenfilename(initialdir = "/",title = "Select file",filetypes = (("jpeg files","*.jpg"),("all files","*.*")))
lb2 = Label(bati, text=bati.filename, fg='red', font=("Times", 10, "bold"))
lb2.place(x='270',y='34')
def first():
b = Button(bati, text='Import', activebackground='red', bd='3', bg='gray', fg='yellow', font=("Times New Roman", 10, "bold"), command=askfile)
b.place(x='200',y='30')
working = True
def second():
if working == True:
b.widget.pack_forget()
working = False
canvas_width = 3000
canvas_height = 220
w = Canvas(bati, width=canvas_width,height=canvas_height)
w.pack()
y = int(canvas_height / 2)
w.create_line(0, y, canvas_width, y, fill="#476042", width='2')
v = IntVar()
v.set('L')
rb1 = Radiobutton(bati, text='Import A File', value=1, variable=v, command=first, font=("Comic Sans MS", 10, "bold"))
rb2 = Radiobutton(bati, text='Choose From Web', value=2, variable=v, command=second, font=("Comic Sans MS", 10, "bold"))
rb1.place(x='50',y='30')
rb2.place(x='50',y='50')
working = False
bati.mainloop()
Issues:
Local variables will not work for this - you need to remember the button state outside of first and second, so that you can use it the next time.
We show the button with .place, so we should similarly hide it with .place_forget instead of .pack_forget.
The .place position should be given with integers instead of strings. Similarly for the button's bd, i.e. border width (in pixels, i.e. a number of pixels).
Event handlers are supposed to receive an event parameter, even if you ignore it. The .widget you've written in your second command is presumably copied from some other code that tries to find out the widget to hide from the event data (e.g. here). But that .widget would be the one that sent the command, i.e. the Radiobutton, not the Button that you want to hide.
What we will do is create the button ahead of time, in a global variable (in a more serious project, you should consider using classes instead, and then you would use a class member to remember it). Since the button should start out not visible, we just don't .place it right away, but instead use .place in first and .place_forget in second. So:
b = Button(
bati, text='Import', activebackground='red', bd=3,
bg='gray', fg='yellow', font=("Times New Roman", 10, "bold"),
command=askfile
)
def first(event):
b.place(x=200,y=30)
def second():
b.place_forget()

How do I get the label position of entry widgets to update properly when changing the font size?

I am creating a tkinter ttk GUI and I made some radio buttons that select the GUI size. This changes, among other things, the global font size for the whole application.
I made a quick script to demonstrate it.
import tkinter as tk
from tkinter import ttk
import tkinter.font
UI_SIZES = ["Tiny", "Small", "Normal", "Large", "Huge"]
TINY_FONT_SIZE = 8
SMALL_FONT_SIZE = 12
NORMAL_FONT_SIZE = 16
LARGE_FONT_SIZE = 18
HUGE_FONT_SIZE = 22
root = tk.Tk()
ui_size = tk.StringVar(root, "Normal")
entry_text = tk.StringVar(root, "Entry Text")
def text_size_callback(*_args):
"""change the program font size when the font_size variable changes"""
font = tkinter.font.nametofont("TkDefaultFont")
selected_size = ui_size.get()
if selected_size == "Tiny":
font.configure(size=TINY_FONT_SIZE)
elif selected_size == "Small":
font.configure(size=SMALL_FONT_SIZE)
elif selected_size == "Normal":
font.configure(size=NORMAL_FONT_SIZE)
elif selected_size == "Large":
font.configure(size=LARGE_FONT_SIZE)
elif selected_size == "Huge":
font.configure(size=HUGE_FONT_SIZE)
root.option_add("*Font", font)
ui_size.trace('w', text_size_callback)
ui_size.set("Normal")
ui_radio_group = ttk.Frame(root)
ui_size_radios = []
for sizeval in UI_SIZES:
ui_size_radios.append(ttk.Radiobutton(
ui_radio_group,
text=sizeval,
variable=ui_size,
value=sizeval
))
text_entry = ttk.Entry(ui_radio_group, textvariable=entry_text)
i = 0
for sizeradio in ui_size_radios:
sizeradio.grid(row=i, column=0, sticky=tk.W)
i += 1
text_entry.grid(row=2, column=1)
ui_radio_group.pack()
root.mainloop()
This works well, except for the labels inside entries (and comboboxes). The text itself resizes but the label does not, until the text is edited. This makes the text entries look weird.
Here it is on startup
Then messed up after clicking tiny
Then fixed after I hit backspace
How can I work around this? Or, how can I do this more correctly so it works without workarounds?
I'm on Windows 10, using Python 3.6.3 64-bit, if it helps.
EDIT: I made an example for you all to poke with
After some tests, it seems to happen only with ttk.Entry, not tk.Entry. There are at least two workarounds:
to insert then delete text in the entry after the font size changed
to reconfigure the font of the entry after the font size changed
Here is an example:
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkfont
def change_fontsize():
font.configure(size=30)
def change_fontsize_1():
font.configure(size=20)
# workaround 1
entry.insert(0, ' ')
entry.delete(0)
def change_fontsize_2():
font.configure(size=5)
# workaround 2
entry.configure(font=font)
root = tk.Tk()
font = tkfont.Font(root)
entry = ttk.Entry(root, font=font)
entry.insert(0, 'Entry Text')
entry.pack()
ttk.Button(root, text='Change size, no workaround', command=change_fontsize).pack()
ttk.Button(root, text='Change size, workaround 1', command=change_fontsize_1).pack()
ttk.Button(root, text='Change size, workaround 2', command=change_fontsize_2).pack()
root.mainloop()

Tkinter - editing a text box using a button

Using Tkinter in Python I am trying to change what is displayed in a textbox when a button is pressed. My code so far is:
screen = Tk()
text = Text(screen, height = 2, width = 30)
text.pack()
text.insert(END, '-')
def apress():
text.insert(END, 'a')
a = Tkinter.Button (screen, text = 'a', width = 5, command = apress).pack()
mainloop()
When the code is run nothing happens and the debugger will not stop running even if you click 'abort debugging'. Is there a way to fix this?
Here's the working code:
from Tkinter import *
screen = Tk()
text = Text(screen, height = 2, width = 30)
text.pack()
text.insert(END, '-')
def apress():
text.insert(END, 'a')
btn = Button(screen, text = 'a', width = 5, command = apress)
btn.pack()
mainloop()
Changes I made:
Added the import from Tkinter import *
Used Button instead of Tkinter.Button - since we used an wildcard import
Button.pack() separated on a new line
Demo:
Initial view:
Clicked the button several times:

Categories

Resources