Python Tkinter updating text-box content via a function - python

Beginner programmer here, working on making a basic GUI as part of a tutorial I was following online, but none of them say how to get a Text box to update using the output of the other parts of your code.
I tried multiple other answers on the site, including one using StringVar's, which got me nowhere, another using a decorator, and the rest seemed way out of my depth.
Here's my code:
import tkinter as tk
import time
#Creating Root
root = tk.Tk()
#GUI TEMPLATE
frame =tk.Frame(root,
height = 100,
width = 400)
frame.pack()
v = StringVar()
colour = ["red","blue","green","white","yellow"]
labels = range(5)
#change number to change how many labels
for i in range(5):
l= tk.Label(root,
text = colour[i],
bg = colour[i])
l.place(x = 10 +i*70, y = 10, width=60, height=25)
T1 = tk.Text(root, height=2, width=40)
words = "Don't name your files after module names!"
T1.insert(tk.END, textvariable=v)
T1.place(x = 10, y= 40)
S = tk.Scrollbar(root)
S.config(command=T1.yview)
S.place(x = 340, y=40)
T1.config(yscrollcommand=S.set)
root.mainloop()
v.set("Something Else!")
Now, what it should output is a row of coloured labels, which works fine, and a text box with a scroll bar, which should instantly update to read 'Something Else!', which does not work fine.
Instead I get the following error:
NameError: name 'StringVar' is not defined
I know what this error means, it's just I've hit a wall when it comes to finding a solution that works for me, and doesn't need a doctorate to understand.
What I'm asking for is if someone can give me a solution that would work for this, and hopefully explain it!
Thanks in advance.
EDIT:
So after fixing the syntax error, and then finding out what I'm trying to do doesn't work, how would I go about this?
Could I use a label instead? Or is there another, better way?
Thanks again!

StringVar should accessed via tk:
v = tk.StringVar()
On another note, tk.Text.insert does not take a textvariable parameter, so the following won't work:
T1.insert(tk.END, textvariable=v)
# ^^^^^^^^^^^^??
From the docs:
Unlike for example the entry widget, text widgets don't support a
"textvariable" configuration option
Also see How can I connect a StringVar to a Text widget in Python/Tkinter? as to why this won't work.

I was experimenting with learning threading with Tkinter and have made something work to insert and update text in a Tkinter text box in Python3 which may be of help.
I found that by deleting the text I could then insert new text. Untill I deleted the existing text the instruction to insert text seamed to be ignored.
{ foo = input("Give me input: " )
self.T.delete("1.0", END) #Clear the text window so we can write.
self.T.insert(END,foo) #Write the new text.}

Related

I want to get a way to highlight code using the python Tkinter Text control

I want to implement a code highlighting function, but I don't know how to use this tk.Text 。 (not necessarily python, of course)
I hope to get a highlighted function f (x, y, z, a) whose function is to highlight it into the a color from row x, column y to column z. Then I also hope that every time the user enters in the text control, I can receive a message and call the another function t (x, y) to update the highlighted content. I hope to get this kind of writing.
In addition, I don't want to use a third-party library to solve this problem. My reference code is as follows. You can modify my code to answer this question.
Reference code:
import tkinter as tk
window = tk.Tk()
window.geometry('600x600')
window.configure(bg='white')
content = tk.Text(window, relief='solid', font=('Consolas', 12))
content.pack()
content.place(x=15, y=15, width=570, height=535)
window.mainloop()
My English is not very well. I use machine to translate my text, so it may contain some low-level errors.
Thanks!
You should use search function to search the first index of you word and then use this "%s+%sc"%(starting_index(that you will find with the help of search function),length of your word)
if you don't want to download any external module then I suggest you to use idlelib this is a build in module
you can try this:-
import tkinter as tk
def highight_function(code_list):
for i in code_list:
content.tag_remove(i,"1.0",tk.END)
starting_index = 1.0
while True:
pattern = r"\m{}\M".format(i)
starting_index = content.search(pattern,starting_index,regexp=True,stopindex=tk.END)
if not starting_index:
break
last_index = "%s+%sc"%(starting_index,len(i))
content.tag_add(i,starting_index,last_index)
starting_index = last_index
content.tag_configure(i,foreground=code_list[i])
window.after(1000,lambda :highight_function(code_list))
window = tk.Tk()
code_list = {"from":"cyan","as":"cyan","import":"cyan"}
window.geometry('600x600')
window.configure(bg='white')
content = tk.Text(window, relief='solid', font=('Consolas', 12))
content.pack()
content.place(x=15, y=15, width=570, height=535)
window.after(1000,lambda :highight_function(code_list))
window.mainloop()

How can I type in multiple lines using Tkinter Entry?

I am quite new to programming and tkinter in general. Here I will show the part of the code which I am struggling with. I thought by making this widget bigger it will let me type in multiple lines, but obviously it didn't. What should I do to have this entry allow the user to type in multiple lines after clicking the "enter" button for example?
tweet_text = tk.Entry(post_window, width = 33)
tweet_text.grid(ipadx = 49, ipady = 49, row = 0 , column = 0)```
Thank you!
Entry widget is used for typing in quite short inputs, for larger inputs use the Text widget.
from tkinter import *
def take():
print(t.get(1.0,'end-1c'))
root = Tk()
t = Text(root,height=10,width=50)
t.pack(padx=10)
b = Button(root,text='Click me',command=take)
b.pack(pady=10)
root.mainloop()
This is a simple code to show and get all the text from the textbox.
For more info on Text widget, click here
Hope this cleared your doubts, do let me know if any errors or doubts.
Cheers

TypeError: object of type 'Text' has no len()

what is this error and how can i fix it?
def check_number():
if (len(txtNum1)!=11):
error_number = "the number that you entered is wrong"
msg = tk.Message(frame, text = error_number , fg="red")
msg.pack()
title = Label(frame, text="enter your number", fg="gray")
title.pack()
txtNum1 = Text (frame, height=1, width=30)
txtNum1.pack(side=tk.TOP)
button = tk.Button(frame,
text="chek",
fg="green",
command=check_number)
button.pack(side=tk.BOTTOM)
root.mainloop()
i just test __len__method but its not working well.
One of the issues in your code is use use of the if statement. You are asking if the Text Object has a length instead of checking the content of the text object. This can be corrected with the use of get(). If you use get() on a Text Box you will need to specify the indices. .get(1.0, "end"). The problem with doing it this way is you will be getting a length that is 1 character longer than what has been typed so the easy fix to this is to just use an entry field here.
With an Entry() field you can use get() without indices and it will get a copy of the text in that field. Keep in mind if you have a space before or after the text it will count that as well. If you want to compensate for this you can add strip() after get() to delete the white-space on either side.
For a little clean up you will want to change how you are creating your message. With your code if you press the button multiple times then the program will add a new message with each button press. This will cause the messages to stack. To avoid this lets create the message label first and then just update it with our function using the .config() method.
The next bit of clean up lets remove the variable assignments to widgets that do not need them. Your first label and the button do not need to be assigned to a variable in this case.
The last bit of clean up is making sure you are consistent with your widgets. Right now (based of your example code) you are importing tkinter twice. Once with from tkinter import * and once with import tkinter as tk. You do not need both and should stick with the 2nd import method only. Using import tkinter as tk will help prevent you from overriding build in methods on accident.
Take a look at my below code:
import tkinter as tk
root = tk.Tk()
def check_number():
msg.config(text = "")
if len(txtNum1.get().strip()) != 11:
error_number = "the number that you entered is wrong"
msg.config(text = error_number)
tk.Label(root, text="enter your number", fg="gray").pack()
txtNum1 = tk.Entry(root, width=30)
txtNum1.pack(side=tk.TOP)
tk.Button(root, text="chek", fg="green", command=check_number).pack(side=tk.BOTTOM)
msg = tk.Message(root, text = "" , fg="red")
msg.pack()
root.mainloop()

How do I run a program within a Tkinter frame?

I'm trying to create a Tkinter app that incorporates the use of a touchscreen keyboard and will be run off a Raspberry Pi. I found an onscreen keyboard called Matchbox-keyboard.
My question is: is there a way to "embed" this keyboard into a GUI created by Tkinter? I would like to embed the keyboard so it opens at the bottom of the parent window.
So far all I can come up with is:
subprocess.Popen(['matchbox-keyboard'])
which works, but it opens in a separate window.
Below is a sample of my code. Keep in mind that I haven't coded the get() functions for the text fields yet, or any of the other functions for that matter.
from tkinter import *
from tkinter import ttk
import subprocess
process_one = subprocess.Popen(['matchbox-keyboard'])
root = Tk()
bottomframe = Frame(root)
bottomframe.pack(side = BOTTOM)
root.title("PinScore")
L0 = Label(root, text = "Welcome to PinScore!")
L0.pack(side = TOP)
L1 = Label(root, text = "Initials:")
L1.pack(side = LEFT)
E1 = Entry(root, bd = 5)
E1.pack(side = RIGHT)
L2 = Label(root, text = "High Score:")
L2.pack( side = RIGHT)
E2 = Entry(root, bd = 5)
E2.pack(side = RIGHT)
B = Button(bottomframe, text = "Enter High Score")
B.pack(side = BOTTOM)
root.mainloop()
The short answer: no, but there is hope, and it will require a fair amount of work. According to the github it is made in gtk. Then the question becomes "Can I put a gtk object in my tkinter program?". To my knowledge (and a lot of Googling) there is no way to embed gtk features in the Tkinter. You may want to try pyGTK instead, because these would be much easier to integrate (I know that it is possible). I might suggest that before you get any further in your project.
Use PyGTK! The github contains the gtk source and you can do it that way.
Actually, looking more at the github, you may not need to do that. The keyboard allows for command-line options, such as -v,--override Absolute positioning on the screen and -g,--geometry <HxW.y.x> Specify keyboard's geometry (taken from the github). You won't be able to control the z position (as in whether it is above or below your window).
If you truely want the embeded feeling, the github also says that you can embed it in gtk and points to examples/matchbox-keyboard-gtk-embed.c this might be what your looking for. You probably can translate it to pygtk. I found this, which talks about XEMBED. And I found this too which actually embeds something. Finally, I'll point you to the docs for gtk.socket.

Python code problem, application has been destroyed Tcl error

I am making a Tkinter GUI to do nothing except call images - and of course, I have struggled to find decent tkinter documentation all along.
There is a line of my code which cannot seem to do as asked - I want to call up all the values in a dictionary and individually print and pull an image by the same name for each one before the next value is called up. I have tried dict.itervalues() and dict.values() and can't seem to figure anything out altogether...
Anyway, here is the snippet:
for key in ansDict.iterkeys(): #using the iterkeys function... kind of
x=key
root = tk.Tk() # root window created (is this in the right place?)
root.title('C H E M I S T R Y A B C\'s')
frameAns=tk.Frame(root)
frameAns.grid(row=0, column=0, sticky=tk.NW)
for i in range(len(ansDict[x])):
print '-->' + ansDict[x][i]
for value in ansDict.itervalues(): #This is the most important part
for i in range(len(value)): #pulls value list from dictionary named ansDict
picRef1 = Image.open(value[i] + '.jpg') #calls image file by the same name using PIL
photo1 = ImageTk.PhotoImage(picRef1, master=root)
button1 = tk.Button(frameAns, compound=tk.TOP, image=photo1, text=str(value[i]) + '\nClose me!', bg='white') #pulls up button onto which the image is pasted
button1.grid(sticky=tk.NW, padx=2, pady=2) #places button on grid
button1.image=photo1
root.mainloop()
Finally, at the end, it pulls up one or two images and then I get the following error:
TclError: can't invoke "image" command: application has been destroyed
and I can't figure out what is wrong. I can't move the image command, and somehow I need to "save" it so it isn't destroyed. I know there are other code errors here, but I think that if I figure out the TclError that I am getting that I can set everything else straight.
If there is an easier way to do all this please do tell!
I have looked around for a good solution to this but have yet to find the proper solution. Looking at the Tkinter.py class it looks like the Image del value is:
def __del__(self):
if self.name:
try:
self.tk.call('image', 'delete', self.name)
except TclError:
# May happen if the root was destroyed
pass
This means if you wanted to do a BRUTAL hack you could setup a PhotoImage as described in jtp's link.
photo = tk.PhotoImage(file="C:/myimage.gif")
widget["image"] = photo
widget.image = photo
Then you could just before the program exited do the following hack:
photo.name = None
This would prevent it from trying to clean itself up in the PhotoImage delete and prevent the exception from being called in the del method. I do not really recommend you do this unless your back is up against the wall, and you have no alternative.
I will continue to look into this and if I find a better solution will edit this post with a better one (hopefully someone will give the correct solution before then).
Here is one possibility, although it is structured differently than your example. It stacks the four 100 pixel square images on top of one another. I believe you need to keep a separate reference to each Image object, so I tucked them away in the images dictionary.
from Tkinter import *
import os
from PIL import Image, ImageTk
image_names = { '1':'one','2':'two','3':'three','4':'four' }
images = {}
root = Tk()
root.title("HELLO")
frm = Frame(root)
for v in image_names.itervalues():
images[v] = {}
images[v]['i'] = Image.open("%s%s.jpg" % (os.path.dirname(__file__), v))
images[v]['pi'] = ImageTk.PhotoImage(images[v]['i'])
images[v]['b'] = Button(frm, image=images[v]['pi'])
images[v]['b'].pack()
frm.pack()
mainloop()
Here is a good link discussing the PhotoImage class.
http://effbot.org/tkinterbook/photoimage.htm
It seems that you did not get the idea of Event-driven programming. You should create whole GUI once, fill it with widgets, setup the events and then enter infinite loop. The GUI should call callback functions based on your event to function binding. So those parts of your program should definitely be called just once: root = tk.Tk(), root.mainloop().
Edit: Added Event-driven programming "idea example".
from Tkinter import *
master = Tk()
def callback():
print "click!"
b = Button(master, text="OK", command=callback)
b.pack()
mainloop()

Categories