Ошибка Tcl_AsyncDelete. Невозможно прекратить Tk - PullRequest
0 голосов
/ 03 сентября 2018

Я использую Tkinter в узле ROS для создания графического интерфейса и публикации значений масштаба в другом узле ROS. Я достиг этого. Проблема возникает, когда я пытаюсь закрыть этот графический интерфейс и перезапустить узел. Сообщение журнала, которое я получаю, выглядит следующим образом:

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)

Согласно это , я думаю, что мне придется прекратить 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 голосов
/ 03 сентября 2018

Проблема в том, что rospy является многопоточным, но Tk очень заинтересован в использовании только из одного потока. (Технически, можно использовать Tk из нескольких потоков - путем соответствующего помещения в карантин оконных объектов и т. Д. - но действительно сложно получить права, и вы, вероятно, этого не хотите.)

Самый простой подход в целом - создать два класса, один из которых просто обрабатывает Tk (с входящими и исходящими сообщениями все в очереди), а другой - соединяет остальные кода. Затем, когда вы хотите, чтобы появился графический интерфейс 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) вместо этого, и это считается опасным по уважительной причине (хотя это может быть необходимо).

...