0

I have a tkinter.Label created inside a function and from a totally seperate part of my code I need to update the text.

I have tried just about every solution google provides over the last hour and I can't get any of them to work, some error, some show blanks, some just fail to do anything.

I am creating the labels as follows

def createWindow():
    window = tkinter.Tk()
    container = tkinter.Frame(window, padx=5, pady=5)
    summaryFrame = tkinter.Frame(container, bd=2, relief='groove')
    summaryFrame.pack(side='top', fill='x')
    summaryUser = tkinter.Label(summaryFrame, text='Some text').grid(row=1, column=1, sticky='w')

Much later I need to change the text of this label but because I'm no longer in this createWindow() function I don't have access to the summaryUser variable that contains the text.

I have tried summaryEvent["text"] (errors because it's not available), I have tried using a global variable and using textvariable=AGlobalVariable instead of text='Some text' (leaves the label text blank) and many other google results all with no success.

This seems like the sort of functionality that should be easier than this...

EDIT 1

I have tried the following...

summaryUserText = 'Some text'

def createWindow():
    global summaryUserText
    window = tkinter.Tk()
    container = tkinter.Frame(window, padx=5, pady=5)
    summaryFrame = tkinter.Frame(container, bd=2, relief='groove')
    summaryFrame.pack(side='top', fill='x')
    summaryUser = tkinter.Label(summaryFrame, textvariable=summaryUserText)
    summaryUser.grid(row=1, column=1, sticky='w')

When I try this the label just starts blank, not with the content of the variable.

EDIT 2

I have also tried the following...

summaryUserText= tkinter.StringVar()
summaryUserText.set('Some text')

def createWindow():
    ...
    summaryUser= tkinter.Label(summaryFrame, textvariable=summaryUserText)

But as soon as python sees the first line it errors with the following...

  File "C:\Program Files\Python37\lib\tkinter\__init__.py", line 480, in __init__
    Variable.__init__(self, master, value, name)
  File "C:\Program Files\Python37\lib\tkinter\__init__.py", line 317, in __init__
    self._root = master._root()
AttributeError: 'NoneType' object has no attribute '_root'

Edit 3

The simplest code that simulates the issue in one complete file

import tkinter

def loadEvent():
    global summaryEventText
    summaryEventText.set('Updated')
    print('Updated')

def createWindow():
    global  summaryEventText
    window = tkinter.Tk()
    summaryEventText = tkinter.StringVar()
    summaryEventText.set('Init')
    summaryEventLabel = tkinter.Label(window, text='Event:').grid(row=0, column=0, sticky='e')
    summaryEvent = tkinter.Label(window, textvariable=summaryEventText).grid(row=0, column=1, sticky='w')
    window.mainloop()

createWindow()
loadEvent()

No errors, the print('Updated') works but the summaryEventText.set('Updated') does nothing.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
Jay2001
  • 59
  • 7
  • You either need to make `summaryUser` globally available (as a global variable or an instance variable, most likely) and call `summaryUser['text'] = 'new text'` later, or use the `textvariable=` option to refer to a `StringVar` that is globally available and call `.set('new text')` on that var later. – jasonharper Sep 16 '19 at 17:00
  • Have you tried returning a value? – Mad Physicist Sep 16 '19 at 17:13
  • *Only* the specific Tkinter "Var" types work for the `textvariable=` option, and of them, `StringVar` is the most likely choice for a Label. So try `summaryUserText = StringVar(); summaryUserText.set('Some text')`. – jasonharper Sep 16 '19 at 17:15
  • I have tried that as well, but as soon as I use the summaryUserText = tkinter.StringVar() I get the following error... AttributeError: 'NoneType' object has no attribute '_root' – Jay2001 Sep 16 '19 at 17:36
  • 1
    Looks like you're trying to create the `StringVar` before having called `Tk()` to initialize things. – jasonharper Sep 16 '19 at 18:05
  • @jasonharper The very first line in the file is "import tkinter, sqlite3" – Jay2001 Sep 16 '19 at 20:57
  • @stovfl I made the changes you suggested and the error went away but it still won't update the label from another function. – Jay2001 Sep 16 '19 at 21:22
  • @Jay2001: `window.mainloop()` is **blocking**, therefore you never reach `loadEvent()`. Your approach turn the usage of `tkinter` upside down. Using `tkinter` the first line should `root = Tk()`, the very last line should `root.mainloop()`. – stovfl Sep 16 '19 at 22:01
  • @stovfl it does reach loadEvent() though because the print('Updated') statement works just fine. – Jay2001 Sep 17 '19 at 07:16
  • @stovfl Are you saying that you can't create the window (ie, run the root = Tk() and root.mainloop() statements) inside a function? – Jay2001 Sep 17 '19 at 07:20
  • @Jay2001: You have to accept, that `.mainloop()` **is blocking**. First you have to understand [Event-driven programming](https://stackoverflow.com/a/9343402/7414759) – stovfl Sep 17 '19 at 07:31

1 Answers1

-1

The short answer is: to change an object you must have a reference to that object. That's not unique to tkinter, it's a fundamental aspect of programming. You're using local variables which by definition means you can't access the widgets outside of that function. The solution, then, is to not use local variables.

A proper solution requires you to save a reference that the other function can access, or provide a function that can return a reference. Have the function return a reference, use a global variable, or use a class variable.

The simplest solution for your specific example is to use a global variable. For example:

import tkinter

def loadEvent():
    ...
    summaryEventLabel.configure(text='Updated')
    ...

def createWindow():
    global  summaryEventLabel
    ...
    summaryEventLabel = tkinter.Label(window, text='Event:')
    summaryEventLabel.grid(row=0, column=0, sticky='e')
    ...

createWindow()
loadEvent()

However, your specific example won't work because window.mainloop() will not return until the window is destroyed or you call its quit method. This means that createWindow won't return, so loadEvent will not be called.

If you were to structure your program to avoid this problem -- for example, calling loadEvent in response to a button click or some other event -- this solution would work.

Here's a working example that updates the label after 5 seconds:

import tkinter

def loadEvent():
    summaryEventLabel.configure(text='Updated')
    print('Updated')

def createWindow():
    global  summaryEventLabel
    window = tkinter.Tk()
    summaryEventText = tkinter.StringVar()
    summaryEventText.set('Init')
    summaryEventLabel = tkinter.Label(window, text='Event:')
    summaryEventLabel.grid(row=0, column=0, sticky='e')
    summaryEvent = tkinter.Label(window, textvariable=summaryEventText).grid(row=0, column=1, sticky='w')
    window.after(5000, loadEvent)
    window.mainloop()

createWindow()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685