python - tkinter Class Object Not Defined
问题描述
I am fairly new to Python and this is my first project using tkinter. I have my entire project working fine with one exception. I have built the tkinter code into a class and it all works but I cannot figure out how to call the methods from outside of the class.
When I create the object on the following line in main, I get NameError: name 'robotGUI' is not defined
class botGUI:
def __init__(self):
#Init Code
def updateStatus(self):
#Code Here
robotGUI = botGUI()
If I initialize the variable "robotGUI" to None, the code runs but when I later try to access one of its methods I get AttributeError: 'NoneType' object has no attribute 'doSomething'. It appears that the robotGUI object is not being created but I do not understand why.
I have searched everywhere and have found some close answers but nothing that exactly pertains to this issue. I have a handfull of other classes that are working perfect in this program so I am sure it has to do with tkinter and its internal mainloop just havent been able to pin point it.
Here is my greatly reduced and simplified code showing the problem:
#!/usr/bin/env python3
#Imports
import socket, select, errno, sys, queue, time, threading, cv2
from tkinter import *
from tkinter import font
from PIL import Image, ImageTk
#GUI
class botGUI:
def __init__(self):
#Create the Window Object and Setup the Window
self.window = Tk()
self.window.geometry("800x480+0+0")
self.window.overrideredirect(True)
self.window.fullScreenState = False
#Code to Generate Gaphics Here .....
#Call Repeating Status Update Script and Start the Main Loop
self.updateStatus()
self.window.mainloop()
def updateStatus(self):
#Code to Handle Updating Screen Objects Here ....
print("Update Status Running")
#Set this function to be called again
self.window.after(1000, lambda: self.updateStatus())
def doSomething(self, myStr):
#Code to change something on the screen ...
print(f"Command: {str(myStr)}")
def doSomethingElse(self, myStr):
#Code to change something on the screen ...
print(f"Command: {str(myStr)}")
#Main Task - Since tKinter is running in the main loop, all of the main loop code is moved to here
def main_loop():
global robotGUI
robotDataReceived = True #This is only for this posting
#Main Loop
while True:
#If Incoming Data from Robot, Get and Process It!
if robotDataReceived:
robotCmdHandler()
#Anti Blocking Delay (Much shorter, set higher for this post)
time.sleep(2)
#Robot Command Handler
def robotCmdHandler():
global robotGUI
#Code to get a command string and process it goes here .....
cmd = "dosomething" #Temporary for this post
#Handle command
if (cmd == "dosomething"):
print("Processing Command")
robotGUI.doSomething("Do This")
if __name__ == '__main__':
global robotGUI
robotGUI = None
#Create and Start Threads
t1 = threading.Thread(target=main_loop, name='t1')
t1.start()
#Create GUI Object
robotGUI = botGUI()
#Wait until threads are finished
t1.join()
解决方案
Remove the call self.window.mainloop()
from botGUI.__init__()
, then you can:
- create the instance of
botGUI
:robotGUI = botGUI()
- create the thread and start it
- call
roboGUI.window.mainloop()
Below is the modified code:
#!/usr/bin/env python3
#Imports
import socket, select, errno, sys, queue, time, threading, cv2
from tkinter import *
from tkinter import font
from PIL import Image, ImageTk
#GUI
class botGUI:
def __init__(self):
#Create the Window Object and Setup the Window
self.window = Tk()
self.window.geometry("800x480+0+0")
self.window.overrideredirect(True)
self.window.fullScreenState = False
#Code to Generate Gaphics Here .....
#Call Repeating Status Update Script and Start the Main Loop
self.updateStatus()
#self.window.mainloop()
def updateStatus(self):
#Code to Handle Updating Screen Objects Here ....
print("Update Status Running")
#Set this function to be called again
self.window.after(1000, lambda: self.updateStatus())
def doSomething(self, myStr):
#Code to change something on the screen ...
print(f"Command: {str(myStr)}")
def doSomethingElse(self, myStr):
#Code to change something on the screen ...
print(f"Command: {str(myStr)}")
#Main Task - Since tKinter is running in the main loop, all of the main loop code is moved to here
def main_loop():
#global robotGUI
robotDataReceived = True #This is only for this posting
#Main Loop
while True:
#If Incoming Data from Robot, Get and Process It!
if robotDataReceived:
robotCmdHandler()
#Anti Blocking Delay (Much shorter, set higher for this post)
time.sleep(2)
#Robot Command Handler
def robotCmdHandler():
#global robotGUI
#Code to get a command string and process it goes here .....
cmd = "dosomething" #Temporary for this post
#Handle command
if (cmd == "dosomething"):
print("Processing Command")
robotGUI.doSomething("Do This")
if __name__ == '__main__':
#Create GUI Object
robotGUI = botGUI()
#Create and Start Threads
t1 = threading.Thread(target=main_loop, name='t1')
t1.start()
# start the GUI main loop
robotGUI.window.mainloop()
#Wait until threads are finished
t1.join()
推荐阅读
- typescript - [Vue 警告]:在渲染期间访问了属性“文本”,但未在 Pug 实例上定义
- python - 2d 数组将 int 更改为 string 并在尝试向其中添加数组时做一些奇怪的事情,而它不应该
- json - 将 numpy 类型嵌套转换为 python(JSON 友好)类型的最简洁方法是什么?
- docker - 使用 docker stack deploy 发布集群时,无法远程访问 Spark master
- reactjs - CellRendererParams 中带有回调的 AG Grid 列未按预期更新父组件数据
- django - 获取 Django 表单字段的人类可读值
- powerbi - 在 Power BI 中以工作区管理员身份重新发布 pbix 报表时,您无权发布到此工作区
- android - 如何结合 API 12 斜线屏幕获得 Firebase 结果?
- git - 发送拉取请求后从远程主分支拉取
- c# - SelectBox 不显示从 ViewData 字典传输的数据