I am trying to get my code to display text from a print statement onto the Tkinter GUI - does anyone know how to do this?
Use this:
import tkinter as tk
# This function acts just like the `print` function:
def print_on_gui(*args, sep=" ", end="\n"):
text = sep.join(args) + end
# Set the Text widget's state to normal so that we can edit its text
text_widget.config(state="normal")
# Insert the text at the end
text_widget.insert("end", text)
# Set the Text widget's state to disabled to disallow the user changing the text
text_widget.config(state="disabled")
# Create a new tkinter window
root = tk.Tk()
# Create a new `Text` widget
text_widget = tk.Text(root, state="disabled")
# Show the widget on the screen
text_widget.pack(fill="both", expand=True)
# Your code should go here
print_on_gui("Hello world!")
print_on_gui("Hello", "world!")
# Go inside tkinter's mainloop
root.mainloop()
The problem with this approach is that if your program runs for too long, it can make the window unresponsive. To avoid that you can use threading but that will complicate things a lot more. If you want to, I can write a solution that uses threading.
Related
First time using Tkinter and I'm trying to make a widget that displays text from a text file and updates the widget when the text in the file changes. I can get a widget to read from a text file, but I can't seem to make it update when the text changes.
Here's the code I'm using right now:
from Tkinter import *
root = Tk()
root.minsize(740,400)
file = open("file location")
eins = StringVar()
data1 = Label(root, textvariable=eins)
data1.config(font=('times', 37))
data1.pack()
eins.set(file.readline())
root.mainloop()
I've searched for help on updating widgets but I can only find help with updating when a button is pressed or an entry widget is used. I was thinking of using a loop that goes through every minute, but wouldn't that just keep creating new widgets?
So you are only reading from file once in your example. As you suggest you need to add some kind of loop to enable you to reread the file often. The problem with normal loops in Tkinter is that they are run in the main thread, making your GUI unresponsive. To get around this, use the Tkinter's after method.
The after method schedules a function to be run after N milliseconds. For example:
from Tkinter import *
# This function will be run every N milliseconds
def get_text(root,val,name):
# try to open the file and set the value of val to its contents
try:
with open(name,"r") as f:
val.set(f.read())
except IOError as e:
print e
else:
# schedule the function to be run again after 1000 milliseconds
root.after(1000,lambda:get_text(root,val,name))
root = Tk()
root.minsize(740,400)
eins = StringVar()
data1 = Label(root, textvariable=eins)
data1.config(font=('times', 37))
data1.pack()
get_text(root,eins,"test.txt")
root.mainloop()
This will loop until the GUI is closed.
I need a widget in TKinter to be a global widget, however, I need the text displayed in it to be different every time. I'm quite new with TKinter and haven't yet successfully managed to edit an option in a widget.
I assume it's something to do with widget.add_option() but the documentation is quite confusing to me and I can't figure out the command.
I specifically just need to edit the text = "" section.
Thanks
EDIT:
gm1_b_current_choice_label = Label(frame_gm1_b, text = "Current input is:\t %s"% str(save_game[6]))
I specifically need to update the save_game[6] (which is a list) in the widget creation, but I assume once the widget is created that's it. I could create the widget every time before I place it but this causes issues with destroying it later.
You can use the .config method to change options on a Tkinter widget.
To demonstrate, consider this simple script:
from Tkinter import Tk, Button, Label
root = Tk()
label = Label(text="This is some text")
label.grid()
def click():
label.config(text="This is different text")
Button(text="Change text", command=click).grid()
root.mainloop()
When the button is clicked, the label's text is changed.
Note that you could also do this:
label["text"] = "This is different text"
or this:
label.configure(text="This is different text")
All three solutions ultimately do the same thing, so you can pick whichever you like.
You can always use the .configure(text = "new text") method, as iCodez suggested.
Alternatively, try using a StringVar as the text_variable parameter:
my_text_var = StringVar(frame_gm1_b)
my_text_var.set("Current input is:\t %s"% str(save_game[6]))
gm1_b_current_choice_label = Label(frame_gm1_b, textvariable = my_text_var)
Then, you can change the text by directly altering my_text_var:
my_text_var.set("Some new text")
This can be linked to a button or another event-based widget, or however else you want to change the text.
I'm working on my first Python program and have little idea what I'm doing. I want to re-bind ctrl-a (control a) to select all text in a Text widget. The current binding is ctrl-/ (control /). The binding part jumps right to the function but the actual text selection doesn't work. Instead, the cursor jumps to the first character on the first line (like it should) and nothing else happens. I'm sure this is embaressingly easy to fix but after spending hour an hours on it, I can't figure out what's wrong.
Python 3, Windows:
from tkinter import *
# Select all the text in textbox (not working)
def select_all(event):
textbox.tag_add(SEL, "1.0", END)
textbox.mark_set(INSERT, "1.0")
textbox.see(INSERT)
# Open a window
mainwin = Tk()
# Create a text widget
textbox = Text(mainwin, width=40, height=10)
textbox.pack()
# Add some text
textbox.insert(INSERT, "Select some text then right click in this window")
# Add the binding
textbox.bind("<Control-Key-a>", select_all)
# Start the program
mainwin.mainloop()
So the new code is...
from tkinter import *
# Select all the text in textbox
def select_all(event):
textbox.tag_add(SEL, "1.0", END)
textbox.mark_set(INSERT, "1.0")
textbox.see(INSERT)
return 'break'
# Open a window
mainwin = Tk()
# Create a text widget
textbox = Text(mainwin, width=40, height=10)
textbox.pack()
# Add some text
textbox.insert(INSERT, "Select some text then right click in this window")
# Add the binding
textbox.bind("<Control-Key-a>", select_all)
textbox.bind("<Control-Key-A>", select_all) # just in case caps lock is on
# Start the program
mainwin.mainloop()
and yes it works flawlessly. Thank you, very much Bryan Oakley. Steven Rumbalski: that's a VERY good point, I've followed your advice as well.
You need to both do the selection and then inhibit the default action by having your function return the string "break".
This is due to how Tkinter processes events. It uses what it calls "bind tags". Even though it looks like you are binding to a widget, you are actually binding to a tag that is the name of the widget. There can also be bindings to the widget class, to the toplevel window that the widget is in, and the tag "all" (plus, you can invent your own tags if you wish).
The default ordering of these tags is from most-specific to least-specific, and events are processed in that order. Meaning, if you have a binding both on the widget (most specific) and the class (less specific), the binding will fire for the widget first, and then for the class binding (and then for the toplevel, and then for "all").
What this means is that by default, a binding on a widget augments rather than replaces a default binding. The good news is, you can inhibit any further bindings from firing by simply returning the string "break", which stops the chain of bindings from doing any more work.
You can do that with a module named pyautogui
Just run the command where you want to add the event,
import pyautogui
..., command=lambda *awargs:pyautogui.hotkey("ctrl","a")
Make sure you install the module. If you are on windows, install it by
pip install pyautogui
I have a question to make.If i have a function inserting an item from a list in a text widget and then doing something with that item,it first finishes processing all the items and then does the insert on the text widget.
Here is a simple code demonstrating what I say:
from Tkinter import*
import Tkinter as tk
list = range(1,1000)
def highlow():
for numbers in list:
text1.insert(END,'Number : '+str(numbers)+'\n')
if numbers>500:
print 'High'
else:
print 'Low'
app=Tk()
app.title("Window Title")
app.geometry('400x400+200+200')
app.resizable(0,0)
button=Button(app,text="Run",font=("Times", 12, "bold"), width=20 ,borderwidth=5, foreground = 'white',background = 'blue',command=highlow)
button.pack()
text1 = Text(app,height = 60,font=("Times", 12))
text1.insert(END,"")
text1.pack(padx=15,pady=1)
app.mainloop()
The text widget (and all widgets) are only refreshed when the event loop is entered. While your code is in a loop, no paint events are processed. The simplest solution is to call update_idletasks to update the screen -- refreshing the screen is considered an "idle" task.
For a little more information, see the answers to the question How do widgets update in Tkinter?
I'm coding a very simple Python Tkinter GUI to drive a command-line python script.
My GUI will run on a Windows host and I want it to display the multi-line plain-text output of the script, which is returned to the GUI as a string containing - of course - line breaks (\n characters).
So, I put a Text widget into the GUI and when my script - in example - returns an output which starts with this substring:
RESULT STATUS: OK- Service is currently running\n\nDETAILS: ...
the text displayed contains black vertical bars (|) whenever there is a \n line break character.
Lines are correctly broken but those strange bars make me think that the \n line break character is not correctly decoded, and I don't want the bars in my displayed output.
Any idea on how to make Tkinter display correctly line-endings? Thanks in advance.
Code
This is the workflow for my GUI:
I click a button, which calls the callMe() callback function
The callMe() function parses the arguments coming from an Entry widget and then invokes the python command-line script
The script returns the above-mentioned string and this is used by the callback to update the text of the Text widget
Here is the code:
#init the GUI elements and the Text widget
from Tkinter import *
root = Tk()
top = Frame(root)
outputFrame = Frame(top)
outputFrame.pack(side='top', padx=20, pady=20)
outputTextArea = Text(outputFrame, yscrollcommand=scrollbar.set, width=150, height=40)
outputTextArea.pack(side='left', expand=YES, fill='both')
#callback executed when the button is clicked
def callMe()
#get parameters
# .....
#command line execution script execution
process = subprocess.Popen(command_line, stdout=subprocess.PIPE, shell=True)
#get script output
matr = process.stdout.readlines()
#from a list of strings to a single string
output = "".join(matr)
#write output into the Text widget
outputTextArea.insert(0.0, output)
It could be an issue of '\r' characters before each '\n' character (you said you're on Windows).
Before updating the widget, try first:
text_output= text_output.replace('\r', '')
(text_output contains the output of your script, whose contents are to be inserted in the widget)
If you give us more information, we can help you more.
When to use the Entry Widget (from http://effbot.org/tkinterbook/entry.htm)
The entry widget is used to enter text strings. This widget allows the user to enter one line of text, in a single font.
To enter multiple lines of text, use the Text widget.
Without seeing your code it's impossible to say for certain what the problem is. You say you use a text widget but the behavior seems consistent with using an entry widget. Do you still see the vertical bars with the following code?
import Tkinter as tk
OUTPUT = "RESULT STATUS: OK- Service is currently running\n\nDETAILS: ... "
root = tk.Tk()
text = tk.Text(root, height=4, width=80)
text.pack(fill="both", expand="true")
text.insert("end", OUTPUT)
root.mainloop()
I solved the question thanks to ΤΖΩΤΖΙΟΥ..it was a matter of /r characters..I guess that when I invoke the subprocess.Popen to run my command-line script, a Windows Command Prompt is open, the script executed, and the standard output stream is returned by the Prompt with /r/n line-endings instead of just /n.
Anyway, I will post the code and GUI workflow all the way...
Workflow
This is the workflow for my GUI:
I click a button, which calls the callMe() callback function
The callMe() function parses the arguments coming from an Entry widget and then invokes the python command-line script
The script returns the above-mentioned string and this is used by the callback to update the text of the Text widget
Code
Here is the code:
#init the GUI elements and the Text widget
from Tkinter import *
root = Tk()
top = Frame(root)
outputFrame = Frame(top)
outputFrame.pack(side='top', padx=20, pady=20)
outputTextArea = Text(outputFrame, yscrollcommand=scrollbar.set, width=150, height=40)
outputTextArea.pack(side='left', expand=YES, fill='both')
#callback executed when the button is clicked
def callMe():
#get parameters
# .....
#command line execution script execution
process = subprocess.Popen(command_line, stdout=subprocess.PIPE, shell=True)
#get script output
matr = process.stdout.readlines()
#from a list of strings to a single string
output = "".join(matr) # <<< now it is: output = "".join(matr).replace('\r', '')
#write output into the Text widget
outputTextArea.insert(0.0, output)
Thanks very much to all of you, guys!