Объект класса tkinter не определен

#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. В коде в вашем ответе есть ошибки синтаксиса и отступов.