Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
312 views
in Technique[技术] by (71.8m points)

python - Tcl_AsyncDelete Error. Unable to terminate Tk

I am using Tkinter in a ROS node to create a GUI and publish the scale values to another ROS Node. I have accomplished this. The problem comes when I try to close this GUI and rerun the node. The log message that I get is as follows:

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)

According to this, I think I will have to terminate Tk from its own thread. But I do not know how to do this. My code is as follows:

#!/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.")

I would be grateful if you could help. Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The problem is that rospy is internally multithreaded yet Tk is very keen on only being used from a single thread. (Technically, it's possible to use Tk from multiple threads — by appropriate quarantining of window objects and so on — but it's really tricky to get right and you probably don't want that.)

The easiest approach in general is to make two classes, one that just handles Tk (with incoming and outgoing messages all queued) and the other which does the bridging into the rest of the code. Then, when you want the Tk GUI to appear you run a thread that just does that and then talk to that thread just by its queues. Which sounds like a lot more work, but you can't defeat Tk's internal awareness of threads other than by keeping it strictly on one thread.

However, it might be enough to change the shutdown sequence a bit to be like this.

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

Assuming that you're in the correct thread. If not, you'll need a direct os._exit(0) instead and that's considered dangerous for good reason (yet it might be necessary).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...