#python #python-3.x #tkinter
#python #python-3.x #tkinter
Вопрос:
Я довольно новичок в Python, и это мой первый проект с использованием tkinter. Весь мой проект работает нормально, за одним исключением. Я встроил код tkinter в класс, и все это работает, но я не могу понять, как вызывать методы извне класса.
Когда я создаю объект в следующей строке в main, я получаю ошибку NameError: имя ‘robotGUI’ не определено
class botGUI:
def __init__(self):
#Init Code
def updateStatus(self):
#Code Here
robotGUI = botGUI()
Если я инициализирую переменную «robotGUI» на None, код запускается, но когда я позже пытаюсь получить доступ к одному из его методов, я получаю AttributeError: объект ‘NoneType’ не имеет атрибута ‘doSomething’. Похоже, что объект robotGUI не создается, но я не понимаю почему.
Я искал везде и нашел несколько близких ответов, но ничего, что точно относится к этой проблеме. У меня есть множество других классов, которые отлично работают в этой программе, поэтому я уверен, что это связано с tkinter, и его внутренний основной цикл просто не смог точно указать на это.
Вот мой значительно сокращенный и упрощенный код, показывающий проблему:
#!/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()
Комментарии:
1. Это проблема времени. Поток запускается и
doSomething
вызывается, когдаbotGUI
он еще не создан.
Ответ №1:
Удалите вызов self.window.mainloop()
из botGUI.__init__()
, тогда вы сможете:
- создайте экземпляр
botGUI
:robotGUI = botGUI()
- создайте поток и запустите его
- вызов
roboGUI.window.mainloop()
Ниже приведен измененный код:
#!/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()
Комментарии:
1. Спасибо @acw1668, что решил проблему. Я не понимал, что цикл window предотвратит создание объекта. Мне нужно еще немного изучить процесс создания объекта.
Ответ №2:
Вы должны определить robotGUI вне всех подобных функций:
robotGUI = None
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)
Комментарии:
1. В коде в вашем ответе есть ошибки синтаксиса и отступов.