I have a class with some variables declared as globals in the first method. Another subsequent method starts a thread, and the problem is that python doesn't recognize those global variables after t.start(). Here is how the program works: 1) user can click "YES"-button on tkinter window 2) program then starts to upload data into database. This step takes a while (2-5 minutes) and to prevent UI from freezing during the upload, the program starts a thread that performs the sql stuff. At same time, the program clears widgets from the window and replaces them with new widgets (a progress bar and a text field). 3) after upload is completed, the program again refresh the tkinter window with new buttons and a scrollbox.
Here is the code snippet:
class Application(tk.Frame):
def __init__(self, parent):
#do some init here..
def initUI(self):
global text1, text2, button_no, button_yes, progress_bar #here are the globals
frame1 = tk.Frame(self)
frame1.pack()
text1 = tk.Label(self, text="Do you want to upload a new log file?", background="white")
button_yes = tk.Button(self, text="YES", command=self.removeButtonYes)
button_no = tk.Button(self, text="NO", command=self.removeButtonNo)
text1.pack()
button_yes.pack()
button_no.pack()
self.pack(fill=tk.BOTH, expand=1)
def removeButtonNo(self):
#do something here
def removeButtonYes(self):
text1.pack_forget() #first three lines clear those original three widgets
button_no.pack_forget()
button_yes.pack_forget()
#then add some text with the progress bar
text2 = tk.Label(self, text="Transferring data. Please wait...", background="white")
text2.pack()
progress_bar = ttk.Progressbar(self, orient="horizontal", length=100, mode="indeterminate")
progress_bar.pack()
progress_bar.start(100)
#initialize a thread to prevent the UI from freezing during sql inserts
t = threading.Thread(target=self.writeLogtoDatabase)
t.start()
def writeLogtoDatabase(self):
#open db connection, upload data and close db
self.clearUI() #call a method to clear the progress bar and info text
def clearUI(self):
text2.pack_forget()
progress_bar.pack_forget()
It just throws following error message:
Exception in thread Thread-1:
Traceback (most recent call last):
File "c:\python27\lib\threading.py", line 810, in __bootstrap_inner
self.run()
File "c:\python27\lib\threading.py", line 763, in run
self.__target(*self.__args, **self.__kwargs)
File "C:\Python27\test\testdb2.py", line 94, in writeLogtoDatabase
self.clearUI()
File "C:\Python27\test\testdb2.py", line 98, in clearUI
text2.pack_forget()
NameError: global name 'text2' is not defined
Why? As you can see, I can call those variables outside the method where they are declared. Has this something to do with threading - a thing, that I am not very familiar with?
Unless I don't forget those text2 and progress bar widgets, they will show up in the last window which is not desired functionality.
You should add global text2
on the removeButtonYes
method (and also for progress_bar
, otherwise you'll have the same problem again). It's completely useless to add a global text2
statement in a function that doesn't define that variable.
Also I don't see the advantage of using a global variable in this case, except that it's really easy to create bugs. Why don't you simply use an instance attribute self.text2
and self.progress_bar
?
the global
statement does not create a global. It simply means that assignments to the given names are to assigned in the global context, not the local context. It also means a name access will get a global variable, rather than a local variable. if you only access a global variable then python is able to figure out the variable is a global rather than a local without the need for a global
statement.
eg.
def adder(to_add):
global total
total = total + to_add
return total
try:
# total does not exist at this point
adder(1)
except NameError as e:
print(e)
total = 10
print(adder(4))
Your problem is that you declared a text2
to be global, but then did not assign to text2
, so the name text2
was never created in the global context
When you later methods assign to text2
they create the name in the method's local context (since text2
is not declared to be global there). One the method ends, the Label
object is dereferenced and garbage collected.
You really have no need to be using globals though. You have an instance of Application
(called self
in your methods) which you can assign your objects to. It's really easy to access these objects outside of your Application
instance if you need to
eg.
app = Application() # assigns some object to text2 on self
app.text2.pack()
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.