can anyone explain why it is not possible to use tk.Entry textvariable if you have multiple GUIs in Python?
Imagine this:
I have a program that opens a GUI where a User can choose a file and that file with the filepath is getting set as the textvariable. This variable is getting set 100% correctly because if I tell my program to execute this GUI part then my Entry gets filled with the textvariable that the user chooses.
NOW why does it not work if I have a GUI that calls this (above) GUI with a button?
The values are not changed it's just 1 more GUI that gets called before the one.
SO this needs to be a problem with Python in general I think - does anyone know a workaround?
Some code:
# Data
catalog_file_path = StringVar()
# Functions
def getCatalogPath():
global catalog_file
catalog_file = filedialog.askopenfile(
mode='r', filetypes=[("Text files", "*.txt")])
catalog_file_path.set(catalog_file.name)
catalog_file.reconfigure(encoding='cp1252')
!! if I print here the value is correct !!
return catalog_file
# File-Path Textbox
textBox_catalog_file_path = Entry(
gui, textvariable=catalog_file_path, width=50)
textBox_catalog_file_path.config(font=guiFont)
textBox_catalog_file_path.grid(row=0, column=1, padx=5, pady=15)
Notice that the code does generally work but the textvariable just doesn't get shown at least if there is a GUI calling the Gui - so multiple GUIs basically. Is there something like a refresh element Funktion or something?
Thanks, Faded.
I am making a small program (first actually) with Tkinter. This program is supposed to have the logos for websites and when pressed opens the website.
google_url = 'https://www.google.com'
def OpenUrl():
webbrowser.open_new(google_url)
button1 = tk.Button(root, command=OpenUrl)
This code functions (after packing/importing everything)
Now this was the code before:
button1 = tk.Button(root, command=webbrowser.open_new('https://www.google.com'))
I am new to Python so I really want to understand why this change worked. Was it Python interpreter that did something? Now, this isn't all of the code that I wrote so if you think I should show that then ask me but this was the difference that allowed the code to work. (Open Google when the button was pressed. Before, when the code was ran, it opened google but didn't open tkinter)
Hooray for your first project!
Your first attempt didn't work because the command arg must be function. webbrowser.open_new('https://www.google.com') is a function that has already been called and thusly evaluated. It now equals open_new()s return value (which is nothing).
The contents of OpenUrl are not evaluated until the buttons is clicked, as a function only runs when called. A lambda (an unnamed, in-line function) would work as well:
button1 = tk.Button(root, command= lambda: webbrowser.open_new('https://www.google.com'))
Happy coding!
I'm sure someone is going to mark this as a duplicate the second I post it, but I assure you, I've been looking for hours. I'm brand new to tkinter so bear with me as I'd like a really straightforward answer if possible. Anything related to this specifically was a bit complex for me right now and I didn't feel it answered my question.
I know how to have a Label update with textvariable and StringVar. However, I'm trying to update an integer and I can't seem to figure it out for some reason. The number updates and prints through the console but I can't figure out the right syntax to get it to show up on the interface. It either just shows 0 (as the default variable shows) or there is no text there at all depending on what I've changed the code to)
So all I'm doing is simply incrementing a number. Let's leave it at that for now. And if anyone has any resources to more straightforward documentation please let me know because it seems tkinter in general is pretty obscure in documentation online as far as I can tell.
my_count = 0
def increase_mycount():
global my_count
increment = int(my_count) + 1
my_count = str(increment)
print(my_count)
Label(root, textvariable=my_count).grid(row=2, column=1)
Button(root, text="+", command=inc_mycount).grid(row=3, column=2)
This is where it's at right now, I've tried changing my_count into an IntVar and also a StringVar and I get an error saying I can't use + with int and intvar or int with stringvar
Is there something really simple I'm missing? I'm struggling finding comprehensive documentation on tkinter. It's easy to find Python information but not this really.. I'm in the process of organizing all the info I'd like into some google docs.
Thank you for any time you give. This seems like it should be a really simple thing to do but I've only worked with engines that update things for me. I'm only use tkinter, a .py file, and cmd for this.
And another note, I can't use .set() for this either it seems, like I could for a string. So I'm just struggling with the syntax unless there is a different method for numbers on labels.
You have to use one of tkinter's variable objects when using textvariable. In this case, IntVar.
import tkinter as tk
root = tk.Tk()
my_count = tk.IntVar()
def increase_mycount():
current = my_count.get()
my_count.set(current+1)
tk.Label(root, textvariable=my_count).grid(row=2, column=1)
tk.Button(root, text="+", command=increase_mycount).grid(row=3, column=2)
root.mainloop()
It doesn't look like it has that attribute, but it'd be really useful to me.
You have to change the state of the Text widget from NORMAL to DISABLED after entering text.insert() or text.bind() :
text.config(state=DISABLED)
text = Text(app, state='disabled', width=44, height=5)
Before and after inserting, change the state, otherwise it won't update
text.configure(state='normal')
text.insert('end', 'Some Text')
text.configure(state='disabled')
Very easy solution is just to bind any key press to a function that returns "break" like so:
import Tkinter
root = Tkinter.Tk()
readonly = Tkinter.Text(root)
readonly.bind("<Key>", lambda e: "break")
The tcl wiki describes this problem in detail, and lists three possible solutions:
The Disable/Enable trick described in other answers
Replace the bindings for the insert/delete events
Same as (2), but wrap it up in a separate widget.
(2) or (3) would be preferable, however, the solution isn't obvious. However, a worked solution is available on the unpythonic wiki:
from Tkinter import Text
from idlelib.WidgetRedirector import WidgetRedirector
class ReadOnlyText(Text):
def __init__(self, *args, **kwargs):
Text.__init__(self, *args, **kwargs)
self.redirector = WidgetRedirector(self)
self.insert = self.redirector.register("insert", lambda *args, **kw: "break")
self.delete = self.redirector.register("delete", lambda *args, **kw: "break")
If your use case is really simple, nbro's text.bind('<1>', lambda event: text.focus_set()) code solves the interactivity problem that Craig McQueen sees on OS X but that others don't see on Windows and Linux.
On the other hand, if your readonly data has any contextual structure, at some point you'll probably end up using Tkinter.Text.insert(position, text, taglist) to add it to your readonly Text box window under a tag. You'll do this because you want parts of the data to stand out based on context. Text that's been marked up with tags can be emphasized by calling .Text.tag_config() to change the font or colors, etc. Similarly, text that's been marked up with tags can have interactive bindings attached using .Text.tag_bind(). There's a good example of using these functions here. If a mark_for_paste() function is nice, a mark_for_paste() function that understands the context of your data is probably nicer.
This is how I did it. Making the state disabled at the end disallows the user to edit the text box but making the state normal before the text box is edited is necessary for text to be inserted.
from tkinter import *
text=Text(root)
text.pack()
text.config(state="normal")
text.insert(END, "Text goes here")
text.config(state="disabled")
from Tkinter import *
root = Tk()
text = Text(root)
text.insert(END,"Some Text")
text.configure(state='disabled')
Use this code in windows if you want to disable user edit and allow Ctrl+C for copy on screen text:
def txtEvent(event):
if(event.state==12 and event.keysym=='c' ):
return
else:
return "break"
txt.bind("<Key>", lambda e: txtEvent(e))
If selecting text is not something you need disabling the state is the simplest way to go. In order to support copying you can use an external entity - a Button - to do the job. Whenever the user presses the button the contents of Text will be copied to clipboard. Tk has an in-build support of handling the clipboard (see here) so emulating the behaviour of Ctrl-C is an easy task. If you are building let's say a console where log messages are written you can go further and add an Entry where the user can specify the number of log messages he wants to copy.
Many mentioned you can't copy from the text widget when the state is disabled. For me on Ubuntu Python 3.8.5 the copying issue turned out to be caused by the widget not having focus on Ubuntu (works on Windows).
I have been using the solution with setting the state to disabled and then switching the state, when I need to edit it programmatically using 1) text.config(state=tkinter.NORMAL) 2) editing the text and 3) text.config(state=tkinter.DISABLED).
On windows I was able to copy text from the widget normally, but on Ubuntu it would look like I had selected the text, but I wasn't able to copy it.
After some testing it turned out, that I could copy it as long as the text widget had focus. On Windows the text widget seems to get focus, when you click it regardless of the state, but on Ubuntu clicking the text widget doesn't focus it.
So I fixed this problem by binding the text.focus_set() to the mouse click event "<Button>":
import tkinter
root = tkinter.Tk()
text0 = tkinter.Text(root, state=tkinter.DISABLED)
text0.config(state=tkinter.NORMAL)
text0.insert(1.0, 'You can not copy or edit this text.')
text0.config(state=tkinter.DISABLED)
text0.pack()
text1 = tkinter.Text(root, state=tkinter.DISABLED)
text1.config(state=tkinter.NORMAL)
text1.insert(1.0, 'You can copy, but not edit this text.')
text1.config(state=tkinter.DISABLED)
text1.bind("<Button>", lambda event: text1.focus_set())
text1.pack()
For me at least, that turned out to be a simple but effective solution, hope someone else finds it useful.
Disabling the Text widget is not ideal, since you would then need to re-enable it in order to update it. An easier way is to catch the mouse button and any keystrokes. So:
textWidget.bind("<Button-1>", lambda e: "break")
textWidget.bind("<Key>", lambda e: "break")
seems to do the trick. This is how I disabled my "line numbers" Text widget in a text editor. The first line is the more powerful one. I'm not sure the second is needed, but it makes me feel better having it there. :)
This can also be done in Frames
from tkinter import *
root = Tk()
area = Frame(root)
T = (area, height=5, width=502)
T.pack()
T.insert(1.0, "lorem ipsum")
T.config(state=DISABLED)
area.pack()
root.mainloop()
You could use a Label instead. A Label can be edited programmatically and cannot be edited by the user.
I have some pretty simple code right now that I am having issues with.
root = Tk()
label1 = Label(root, text ="Enter String:")
userInputString = Entry(root)
label1.pack()
userInputString.pack()
submit = Button(root,text = "Submit", command = root.destroy)
submit.pack(side =BOTTOM)
root.mainloop()
print(userInputString)
When I run the code everything operates as I would expect except
print(userInputString)
for an input asdf in the Entry print will return something like 0.9355325
But it will never be the same value back to back always random.
I am using python 3.5 and Eclipse Neon on a Windows 7 Machine.
Ultimately the goal is to accept a string from the user in the box that pops up and then be able to use that value as string later on. For example, it might be a file path that needs to be modified or opened.
Is Entry not the correct widget I should be using for this? Is there something inherently wrong with the code here? I am new to python and don't have a lot of strong programming experience so I am not even certain that this is set up right to receive a string.
Thanks in advance if anyone has any ideas.
There are two things wrong with your print statement. First, you print the widget, not the text in the widget. print(widget) prints str(widget), which is the tk pathname of the widget. The '.' represents the root window. The integer that follows is a number that tkinter assigned as the name of the widget. In current 3.6, it would instead be 'entry', so you would see ".entry".
Second, you try to print the widget text after you destroy the widget. After root.destroy, the python tkinter wrapper still exists, but the tk widget that it wrapped is gone. The following works on 3.6, Win10.
import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="Enter String:")
entry = tk.Entry(root)
def print_entry(event=None):
print(entry.get())
entry.bind('<Key-Return>', print_entry)
entry.focus_set()
submit = tk.Button(root, text="Submit", command=print_entry)
label.pack()
entry.pack()
submit.pack()
root.mainloop()
Bonus 1: I set the focus to the entry box so one can start typing without tabbing to the box or clicking on it.
Bonus 2: I bound the key to the submit function so one can submit without using the mouse. Note that the command then requires an 'event' parameter, but it must default to None to use it with the button.
The NMT Reference, which I use constantly, is fairly complete and mostly correct.