Tcl_AsyncDelete错误。无法终止Tk

时间:2018-09-03 06:39:30

标签: python tkinter tcl rospy

我正在ROS节点中使用Tkinter来创建GUI并将比例值发布到另一个ROS节点。我已经做到了。当我尝试关闭此GUI并重新运行该节点时,就会出现问题。我收到的日志消息如下:

Exception RuntimeError: 'main thread is not in main loop' in <bound method DoubleVar.__del__ of <Tkinter.DoubleVar instance at 0x7f19ea0c3ab8>> ignored
Tcl_AsyncDelete: async handler deleted by the wrong thread
Aborted (core dumped)

根据https://github.com/h2oai/h2o-kubeflow,我认为我必须从其自己的线程终止Tk。但是我不知道该怎么做。我的代码如下:

#!/usr/bin/env python
import rospy
from std_msgs.msg import Float64MultiArray
from Tkinter import *  
from calibration_camera_lidar.msg import Euler_val
import tkMessageBox

class slider():

    def __init__(self):
            rospy.loginfo("init") 
            rospy.init_node('slider', anonymous=True, disable_signals=True)    
            self.spub = rospy.Publisher('Slider_values', Euler_val, queue_size=10)
            self.final_ev = Euler_val()                    
            self.listener()                             

    def listener(self):

            rospy.Subscriber("Euler_values", Float64MultiArray, self.callback)
            rospy.spin()

    def callback(self, data):  

                self.eulerval = list(data.data)
                self.final_ev.Euler_angles = [self.eulerval[0], self.eulerval[1], self.eulerval[2]]
                self.spub.publish(self.final_ev)
                rospy.loginfo(self.final_ev)               
                self.slider_value()

    def callback_exit(self):
            if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
                self.root.destroy()
                self.root.quit()
                rospy.signal_shutdown("shutdown")

    def slider_value(self):

                self.root = Tk()
                self.root.title("fine tune")
                self.root.protocol("WM_DELETE_WINDOW", self.callback_exit)
                self.y_var = DoubleVar()

                self.y_scale = Scale( self.root, from_=self.eulerval[0]-1, to=self.eulerval[0]+1, length=300, label="yaw", resolution=0.0000000000001, variable = self.y_var, orient=HORIZONTAL, command=self.pub_y)
                self.y_scale.set(self.eulerval[0])
                self.y_scale.pack(anchor=CENTER)

                self.label = Label(self.root)
                self.label.pack()    
                self.root.mainloop()

    def pub_y(self, val_y):

            self.eulerval[0] = float(self.y_scale.get())
            self.final_ev.Euler_angles = [self.eulerval[0], self.eulerval[1], self.eulerval[2]]
            self.spub.publish(self.final_ev)
            rospy.loginfo(self.final_ev)

if __name__ == '__main__':
    try:
           slider()

    except:
           rospy.loginfo("Node terminated.")

如果您能提供帮助,我将不胜感激。谢谢!

1 个答案:

答案 0 :(得分:0)

问题在于rospy在内部是多线程的,而Tk非常渴望仅在单个线程中使用。 (从技术上讲,可以通过适当隔离窗口对象等在多个线程中使用Tk,但是真的很棘手,这样做很正确,您可能不希望这样。)

一般而言,最简单的方法是制作两个类,一个是 just 处理Tk(传入和传出消息 all 已排队),另一个是进行桥接其余的代码。然后,当您希望显示Tk GUI时,运行一个仅执行此操作的线程,然后仅通过其队列与该线程进行对话。听起来还有很多工作要做,但是除了严格将Tk保留在一个线程上之外,您无法击败Tk的内部线程意识。

但是可能足以改变关闭顺序,就像这样。

    def callback_exit(self):
        if tkMessageBox.askokcancel("Quit", "Do you really wish to quit?"):
            self.root.destroy()
            rospy.signal_shutdown("shutdown")
            sys.exit(0)

假设您处于正确的线程中。如果不是,那么您将需要直接使用os._exit(0),这被认为是危险的,但有充分的理由(尽管有必要)。