鱼C论坛

 找回密码
 立即注册
查看: 20469|回复: 15

[已解决]thinter 窗口关闭时出错 main thread is not in main loo

[复制链接]
发表于 2016-8-9 14:41:37 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
用 matplotlib 配合 tkinter 做了一个数据实时曲线显示窗口,窗口关闭时弹出RuntimeError: main thread is not in main loop
程序里开启了线程thread1  用来重画曲线  
关闭窗口时 ,主线程关闭,thread1 还在运行  
我觉得是因为这个报错
该怎么解决啊?  

代码如下
from tkinter import *
from tkinter import font
import time
from pylab import *
import matplotlib
import random
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import numpy as np
from threading import Thread

class jiemian(Tk):
    '''
    主窗口
    '''
    def __init__(self):
        '''
        初始化
        '''
        super().__init__()
        self.wm_title('实时绘图测试')
        self.stime = time.time()
        self.ltime = time.time()
        self.createWidgets()

    def createWidgets(self):
        '''
        界面
        '''
        # 设置字体
        self.ft1 = font.Font(family='微软雅黑', size=40, weight=font.BOLD)
        self.ft2 = font.Font(family='微软雅黑', size=15, weight=font.BOLD)
        self.ft3 = font.Font(family='微软雅黑', size=10, weight=font.BOLD)
        self.ft4 = matplotlib.font_manager.FontProperties(fname = 'c:/Windows/Fonts/msyh.ttf')

        self.float_mynumber = DoubleVar()
        self.str_mynumber = StringVar()
        self.float_number = 1.0
        self.int_x = [0]
        self.int_y1 = [0]
        self.int_y2 = [0]
        self.str_time = [0]

        fig= Figure(figsize=(12,4), dpi=100)
        self.ax = fig.add_subplot(111)
        self.thelabel_tittle = Label(self, text="实时绘图测试",font=self.ft1)
        self.thelabel_number = Label(self,textvariable = self.str_mynumber,relief=SUNKEN,state=ACTIVE,padx=20,pady=10, width = 10)
        self.thelabel_time = Label(self, textvariable=self.str_mynumber, relief=SUNKEN, state=ACTIVE, padx=20,
                                     pady=10, width=10)

        self.canvas = FigureCanvasTkAgg(fig , master = self)
        self.canvas.get_tk_widget().grid(row=10,columnspan=30,padx=100,pady=10)
        self.canvas._tkcanvas.grid(row=10,columnspan=30,padx=100,pady=10)

        #toolbar = NavigationToolbar2TkAgg(self.canvas, self).get_tk_widget().grid(row=30,columnspan=10,padx=100,pady=10)
        #toolbar.get_tk_widget().grid(row=30,columnspan=10,padx=100,pady=10)
        #toolbar.update()
        #self.thecanvas_m = Canvas(self,width=400,height = 400)
        self.thescale = Scale(self,orient = HORIZONTAL, length = 284, from_ =0, to  = 250,tickinterval = 50,
                              variable = self.str_mynumber,)

        self.thelabel_tittle.grid(row=0,columnspan=100,padx=100,pady=10)
        self.thelabel_number.grid(row=5, columnspan=10, padx=100, pady=10)
        #self.thecanvas_m.grid(row=10,columnspan=30,padx=100,pady=10)
        self.thescale.grid(row=20, columnspan=100, padx=100, pady=10)

        self.mythread = Thread(target=self.myrun, name='thread-1', daemon=True)
        self.mythread.start()

        self.mainloop()
    def mydraw(self):
        '''绘图逻辑'''
        x = np.random.randint(0,50,size = 100)
        y = np.random.randint(0,50,size = 100)
        self.ax.clear()
        if len(self.int_x) == len(self.int_y1) and len(self.int_x) == len(self.int_y2):
            self.ax.plot(self.int_x,self.int_y1,linestyle = '-',color = 'r',label = u'数值')
            self.ax.plot(self.int_x, self.int_y2, 'g--', label=u'数值')
            #if len(self.int_x)>100:
                #list_temp = [n for n in self.int_x[-100:] if n%5==0]
                #self.ax.set_xticks([n for n in self.int_x[-100:] if n%5==0])
            #self.ax.set_xticklabels(self.str_time)
            locs,labels =plt.xticks()
            plt.xticks(locs,self.str_time)

            self.ax.set_xlabel('时间',fontproperties = self.ft4)
            self.ax.legend(loc = 'best',prop = self.ft4)
        self.canvas.show()
    def myrun(self):
        while 1 :
            self.mydraw()

            nowtime = time.time()
            if (nowtime - self.ltime) >=1:

                if len(self.int_x)>=100:
                    self.int_x = self.int_x[1:]
                self.int_x.append(int(nowtime - self.stime))
                self.str_time.append(time.strftime("%X",time.localtime()))
                if len(self.int_y1) >= 100:
                    self.int_y1 = self.int_y1[1:]
                self.int_y1.append(int(self.str_mynumber.get()))
                if len(self.int_y2) >= 100:
                    self.int_y2 = self.int_y2[1:]
                self.int_y2.append(random.randint((int(self.str_mynumber.get())-10),(int(self.str_mynumber.get())+10)))
                self.ltime = nowtime

if __name__ == '__main__':
    jinmian = jiemian()



报错信息:
Exception in thread thread-1:
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\threading.py", line 914, in _bootstrap_inner
    self.run()
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\threading.py", line 862, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\Administrator\Desktop\新建文本文档 (2).txt", line 93, in myrun
    self.mydraw()
  File "C:\Users\Administrator\Desktop\新建文本文档 (2).txt", line 90, in mydraw
    self.canvas.show()
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 356, in draw
    self._master.update_idletasks()
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1030, in update_idletasks
    self.tk.call('update', 'idletasks')
_tkinter.TclError: can't invoke "update" command: application has been destroyed



运行环境  
win7     python3.5
最佳答案
2016-8-9 22:59:57
SixPy 发表于 2016-8-9 18:04
在绘图时加个 异常判断。

这样写只是掩盖了问题,并没有解决问题。
提供一种结束线程的方法吧~
  1. import threading  
  2. import time

  3. cond= threading.Condition() # 条件锁
  4. isStoped=False

  5. def thd(cond):
  6.     print('开始')
  7.     n=0
  8.     while 1:
  9.         cond.acquire() # 锁
  10.         n+=1
  11.         print(n) # 要完成的事      
  12.         cond.wait(1) # 休眠 1秒
  13.         if isStoped:break # 退出线程循环
  14.         cond.release() # 解锁
  15.         
  16.     cond.release() # 解锁
  17.     print('结束')
  18.    
  19. #启动线程
  20. threading.Thread(target=thd,args=(cond,)).start()
  21. time.sleep(10) # 等10秒,让线程干事

  22. cond.acquire()
  23. isStoped=True # 设置结束标志
  24. cond.notify() # 唤醒休眠的线程,立即结束。
  25. cond.release()
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-8-9 15:10:26 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-8-9 15:28:21 | 显示全部楼层
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2016-8-9 17:56:22 | 显示全部楼层
Python 的线程库不支持 在主线程中结束子线程,理由是我们不应该强制地结束一个线程,这样会带来很多隐患,应该让该线程自己结束自己。所以在 Python 中,推荐的一种方法是在子线程中循环判断一个标志位,在主线程中改变该标志位,子线程读到标志位改变,就结束自己。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-9 18:04:08 | 显示全部楼层
在绘图时加个 异常判断。
  1.     def myrun(self):
  2.         while 1 :
  3.             try:
  4.                 self.mydraw()
  5.             except:
  6.                 return # 发生异常就退出线程
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-9 22:59:57 | 显示全部楼层    本楼为最佳答案   
SixPy 发表于 2016-8-9 18:04
在绘图时加个 异常判断。

这样写只是掩盖了问题,并没有解决问题。
提供一种结束线程的方法吧~
  1. import threading  
  2. import time

  3. cond= threading.Condition() # 条件锁
  4. isStoped=False

  5. def thd(cond):
  6.     print('开始')
  7.     n=0
  8.     while 1:
  9.         cond.acquire() # 锁
  10.         n+=1
  11.         print(n) # 要完成的事      
  12.         cond.wait(1) # 休眠 1秒
  13.         if isStoped:break # 退出线程循环
  14.         cond.release() # 解锁
  15.         
  16.     cond.release() # 解锁
  17.     print('结束')
  18.    
  19. #启动线程
  20. threading.Thread(target=thd,args=(cond,)).start()
  21. time.sleep(10) # 等10秒,让线程干事

  22. cond.acquire()
  23. isStoped=True # 设置结束标志
  24. cond.notify() # 唤醒休眠的线程,立即结束。
  25. cond.release()
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-8-11 11:43:49 | 显示全部楼层
isStoped=True # 设置结束标
这个放在主线程什么地方合适啊?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 12:19:29 | 显示全部楼层
wzy1986 发表于 2016-8-11 11:43
isStoped=True # 设置结束标
这个放在主线程什么地方合适啊?

4楼~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-8-11 14:40:37 | 显示全部楼层
应该是在点击右上角“×”的时候结束子线程  如何获取这个事件啊
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 14:51:40 | 显示全部楼层
本帖最后由 SixPy 于 2016-8-11 14:56 编辑
wzy1986 发表于 2016-8-11 14:40
应该是在点击右上角“×”的时候结束子线程  如何获取这个事件啊


def 结束线程(event):
    pass

root.bind('<Destroy>', 结束线程)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-8-11 16:36:26 | 显示全部楼层
我又写了一个小程序  不报错  程序不能正常关闭了
  1. from tkinter import *
  2. import threading


  3. class jiemian(Tk):
  4.     def __init__(self):
  5.         super().__init__()
  6.         self.title('试验')
  7.         self.int_n = IntVar()
  8.         self.thelabel = Label(self,textvariable = self.int_n ,padx = 10,pady=20).pack()
  9.         self.mythread = threading.Thread(target= self.myrun,name= 'thread_1')
  10.         self.cond = threading.Condition()
  11.         self.isstoped = False
  12.         self.mythread.start()
  13.     def myrun(self):
  14.         while 1:
  15.             self.cond.acquire()  # 锁
  16.             self.int_n.set(self.int_n.get()+1)
  17.             self.cond.wait(0.2)
  18.             if self.int_n.get()>=100:
  19.                 self.int_n.set(0)
  20.             if self.isstoped: break  # 退出线程循环
  21.             self.cond.release()
  22.         self.cond.release()  # 解锁


  23. if __name__ == '__main__':
  24.     jiemian=jiemian()
  25.     def 结束线程(event):
  26.         jiemian.cond.acquire()
  27.         jiemian.isstoped = True  # 设置结束标志
  28.         jiemian.cond.notify()
  29.     jiemian.bind('<Destroy>', 结束线程)
  30.     jiemian.mainloop()
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 16:50:49 | 显示全部楼层
本帖最后由 SixPy 于 2016-8-11 16:52 编辑
wzy1986 发表于 2016-8-11 16:36
我又写了一个小程序  不报错  程序不能正常关闭了


    def 结束线程(event):
        jiemian.cond.acquire()
        jiemian.isstoped = True  # 设置结束标志
        jiemian.cond.notify()
        jiemian.cond.release()

========

    def myrun(self):
        while 1:
            self.cond.acquire()  # 锁
            self.int_n.set(self.int_n.get()+1)
            if self.int_n.get()>=100:
                self.int_n.set(0)
            self.cond.wait(0.2) # 休眠 和 break 之间不能有其他语句,唤醒后立即结束。
            if self.isstoped: break  # 退出线程循环
            self.cond.release()
        self.cond.release()  # 解锁

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2016-8-11 17:29:16 | 显示全部楼层
self.cond.wait(1)   #关闭没问题
self.cond.wait(0.2)  #关闭时好,时坏
self.cond.wait(0.1)  #就管不了
这是啥原因啊,如果想数据自增快一点咋办?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 17:52:47 | 显示全部楼层
wzy1986 发表于 2016-8-11 17:29
self.cond.wait(1)   #关闭没问题
self.cond.wait(0.2)  #关闭时好,时坏
self.cond.wait(0.1)  #就管 ...

tk 处理消息是有点延迟
不适合作高速同步的显示界面
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 18:18:28 | 显示全部楼层
本帖最后由 SixPy 于 2016-8-11 18:19 编辑
SixPy 发表于 2016-8-11 17:52
tk 处理消息是有点延迟
不适合作高速同步的显示界面


&#160; &#160; &#160; &#160;&#160;n=0
        while 1:
            self.cond.acquire()  # 锁

            n+=1
            print('  \r%2d' % (n%100),end='')
            self.cond.wait(0.1)
            if self.isstoped: break  # 退出线程循环
            self.cond.release()
        self.cond.release()  # 解锁

-------------
不在tk控件里显示,就没问题了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2016-8-11 18:40:05 | 显示全部楼层
wzy1986 发表于 2016-8-11 17:29
self.cond.wait(1)   #关闭没问题
self.cond.wait(0.2)  #关闭时好,时坏
self.cond.wait(0.1)  #就管 ...

        n=0
        while 1:
            self.cond.acquire()  # 锁
            n+=1
            self.int_n.set(n%100)
            self.cond.wait(0.1)
            if self.isstoped: break  # 退出线程循环
            self.cond.release()
        self.cond.release()  # 解锁

--------------------------------
还是你的代码写的有问题,效率太低,频繁存取控件对象的属性值。
像我这样写,就没问题了~~




想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-20 19:21

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表