关闭我的tkinter串行应用程序,给了我一个例外 [英] Closing my tkinter serial app, gives me an exception

查看:55
本文介绍了关闭我的tkinter串行应用程序,给了我一个例外的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个程序,它从串行中获取数据并将其显示在tkinter帧上.

这是代码:

 将tkinter导入为tk将tkinter.ttk导入为ttk导入serial.tools.list_ports从tkinter导入scrolledtext#vid的新内容导入时间导入序列导入线程导入continuous_threading#在画布上使用高度= 700宽度= 800#硬编码波特率baudRate = 9600ser = serial.Serial('COM16',baudRate)val1 = 0索引= []def readSerial():全局val1ser_bytes = ser.readline()ser_bytes = ser_bytes.decode("utf-8")val1 = ser_bytesscrollbar.insert("end",val1)t1 = Continuous_threading.PeriodicThread(0.1,readSerial)#----------------------------------------------------------------------#  - - 职能  - -#以下两个功能用于第1帧上的串行端口选择def serial_ports():返回serial.tools.list_ports.comports()def on_select(event = None):全球COMPortCOMPort = cb.get()打印(COMPort)#  - - 职能  - -#--------------------------------------------------------------------------------------------------------------#  - - 主要的  - -root = tk.Tk()#在此创建tkinter窗口root.title(传感器接口")#我们使用画布作为占位符,以获取初始屏幕尺寸(我们定义了HEIGHT和WIDTH)canvas = tk.Canvas(root,height = HEIGHT,width = WIDTH)canvas.pack()#我们使用框架来组织屏幕中的所有小部件#---框架1 ---frame1 = tk.Frame(根)frame1.place(relx = 0,depend = 0.05,relheight = 0.03,relwidth = 1,anchor ='nw')#我们使用relheight和relwidth填充任何父对象-在这种情况下-根label0 = tk.Label(frame1,text =选择设备插入的COM端口:")label0.config(font =("TkDefaultFont",8))label0.place(relx = 0.1,depend = 0.3,relwidth = 0.3,relheight = 0.5)cb = ttk.Combobox(frame1,values = serial_ports())cb.place(relx = 0.5,depend = 0.5,anchor ='center')#将功能分配给cmboboxcb.bind('< ComboboxSelected>',on_select)#---框架1 ---#---框架2 ---frame2 = tk.Frame(root,bg ='#80c1ff')#稍后删除颜色frame2.place(relx = 0,depend = 0.1,relheight = 1,relwidth = 1,anchor ='nw')#制作一个滚动条scrollbar = scrolledtext.ScrolledText(frame2)scrollbar.place(relx = 0,depend = 0,relheight = 1,relwidth = 1,anchor ='nw')#---框架2 ---#--------------------------------------------------------------------------------------------------------t1.start()root.mainloop()#在这里运行我们的应用程序 

当我终止生成的GUI时,我在终端上收到此异常:

线程Thread-1中的

  Exception:致命的Python错误:无法获取< _io.BufferedWriter name ='< stderr的锁>'>在解释器关闭时,可能是由于守护程序线程Python运行时状态:完成(tstate = 005C8F38)线程0x00001a30(最近的呼叫优先):文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py",invoke_excepthook中的第1202行文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py,_ bootstrap_inner中的第934行文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py,_ bootstrap中的第890行当前线程0x00001918(最新调用优先):<没有Python框架> 

当我使用Control + C从终端将其终止时,我得到:

  Traceback(最近一次通话最近):在< module>中的文件"mySerial.py"第110行.root.mainloop()#在这里运行我们的应用程序文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ tkinter \ __ init__.py,第1420行,位于主循环中self.tk.mainloop(n)键盘中断线程Thread-1中的异常:致命的Python错误:无法获取< _io.BufferedWriter name ='< stderr的锁>'>在解释器关闭时,可能是由于守护程序线程Python运行时状态:完成(tstate = 00578F38)线程0x00001ad0(最近的调用优先):文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py",invoke_excepthook中的第1202行文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py,_ bootstrap_inner中的第934行文件"C:\ Users \ User1 \ AppData \ Local \ Programs \ Python \ Python38-32 \ lib \ threading.py,_ bootstrap中的第890行当前线程0x000013d8(最新调用优先):<没有Python框架> 

为什么会发生这种情况?

这是我的代码和错误消息,尝试AST的建议:

 将tkinter导入为tk将tkinter.ttk导入为ttk导入serial.tools.list_ports从tkinter导入scrolledtext导入时间导入序列导入线程导入continuous_threading#在画布上使用高度= 700宽度= 800#硬编码波特率baudRate = 9600#标志在应用程序终止时通知停止=假ser = serial.Serial('COM16',baudRate)val1 = 0def readSerial():全局val1,停止如果不停止:ser_bytes = ser.readline()ser_bytes = ser_bytes.decode("utf-8")val1 = ser_bytesscrollbar.insert("end",val1)别的:返回t1 = Continuous_threading.PeriodicThread(0.1,readSerial)#----------------------------------------------------------------------#  - - 职能  - -#以下两个功能用于第1帧上的串行端口选择def serial_ports():返回serial.tools.list_ports.comports()def on_select(event = None):全球COMPortCOMPort = cb.get()打印(COMPort)def on_close():全球止损停止=真root.destroy()#  - - 职能  - -#--------------------------------------------------------------------------------#  - - 主要的  - -root = tk.Tk()#在此创建tkinter窗口root.title(传感器接口")root.protocol('WM_DELETE_WINDOW',on_close)#我们使用画布作为占位符,以获取初始屏幕尺寸(我们定义了HEIGHT和WIDTH)canvas = tk.Canvas(root,height = HEIGHT,width = WIDTH)canvas.pack()#我们使用框架来组织屏幕中的所有小部件#---框架1 ---frame1 = tk.Frame(根)frame1.place(relx = 0,depend = 0.05,relheight = 0.03,relwidth = 1,anchor ='nw')#我们使用relheight和relwidth填充任何父对象-在这种情况下-根label0 = tk.Label(frame1,text =选择设备插入的COM端口:")label0.config(font =("TkDefaultFont",8))label0.place(relx = 0.1,depend = 0.3,relwidth = 0.3,relheight = 0.5)cb = ttk.Combobox(frame1,values = serial_ports())cb.place(relx = 0.5,depend = 0.5,anchor ='center')#将功能分配给cmboboxcb.bind('< ComboboxSelected>',on_select)#---框架1 ---#---框架2 ---frame2 = tk.Frame(root,bg ='#80c1ff')#稍后删除颜色frame2.place(relx = 0,depend = 0.1,relheight = 1,relwidth = 1,anchor ='nw')#制作一个滚动条scrollbar = scrolledtext.ScrolledText(frame2)scrollbar.place(relx = 0,depend = 0,relheight = 1,relwidth = 1,anchor ='nw')#---框架2 ---#--------------------------------------------------------------------------------t1.start()root.mainloop()#在这里我们运行我们的应用程序 

我得到的错误是我作为代码列出的第一个错误.

当我执行AST的第二种方法时:

如果我关闭GUI(按X),它会关闭且没有任何错误,但是提示卡在终端上-我无法键入任何内容,甚至键盘退出键(Control + C)也无法工作.

如果程序运行并且我在终端(Control + C)的键盘出口退出了,我会收到键盘中断错误(第二个错误已列为代码)

解决方案

根据我的说法,发生这种情况是因为在应用程序被销毁后可能会有一个调度的线程被执行,因此无法找到它具有的GUI元素更新.

我还没有尝试运行您的代码,但是我认为这可能会有所帮助

将窗口删除与更新标记( stop )

的功能相关联

  def on_close():全球止损停止=真root.destroy()root.protocol('WM_DELETE_WINDOW',on_close)停止=假 

并修改 readSerial 函数以检查是否相同

  def readSerial():全局val1,停止如果不停止:ser_bytes = ser.readline()ser_bytes = ser_bytes.decode("utf-8")val1 = ser_bytesscrollbar.insert("end",val1)别的:返回 

编辑

这是 threading.py 中的引发异常的函数.

  def _bootstrap(自己):#包裹忽略的实际引导程序代码#解释程序清理期间的异常.那些通常#发生在守护进程线程不幸地醒来时#瞬间,发现周围的世界被摧毁,并引发了一些#随机异常***尝试在以下位置报告异常时#***下方的_bootstrap_inner().那些随机的例外#不会帮助任何人,他们会使用户感到困惑,因此我们禁止# 他们.只有当世界似乎出现时,我们才会压制他们#确实已经被销毁,因此在#_bootstrap_inner()在正常工作时间正常#个报告.同样,我们仅将它们禁止用于守护线程.#如果非守护进程遇到此问题,则其他地方有问题.尝试:self._bootstrap_inner()除了:如果self._daemonic和_sys为None:返回增加 

在这种情况下,当所有 non-daemonic 线程都被杀死时,可以杀死该线程,您可以尝试将线程设置为 daemonic ,这样该异常是自动处理的.我认为这种方法不需要标记.

  t1.daemon = Truet1.start() 

I have this program that grabs data from serial and displays them on a tkinter frame.

This is the code:

import tkinter as tk
import tkinter.ttk as ttk
import serial.tools.list_ports
from tkinter import scrolledtext 
#new stuff from vid
import time
import serial
import threading
import continuous_threading


#to be used on our canvas
HEIGHT = 700
WIDTH = 800

#hardcoded baud rate
baudRate = 9600



ser = serial.Serial('COM16', baudRate)
val1 = 0
index = []
def readSerial():
    global val1
    ser_bytes = ser.readline()
    ser_bytes = ser_bytes.decode("utf-8")
    val1 = ser_bytes
    scrollbar.insert("end", val1)
    
t1 = continuous_threading.PeriodicThread(0.1, readSerial)
#----------------------------------------------------------------------

# --- functions ---

#the following two functtions are for the seria port selection, on frame 1
def serial_ports():    
    return serial.tools.list_ports.comports()

def on_select(event=None):

    global COMPort
    COMPort = cb.get()
    print(COMPort)



# --- functions ---


#--------------------------------------------------------------------------------------------------------------
# --- main ---
root = tk.Tk() #here we create our tkinter window
root.title("Sensor Interface")

#we use canvas as a placeholder, to get our initial screen size (we have defined HEIGHT and WIDTH)
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()

#we use frames to organize all the widgets in the screen
# --- frame 1 ---
frame1 = tk.Frame(root)
frame1.place(relx=0, rely=0.05, relheight=0.03, relwidth=1, anchor='nw') #we use relheight and relwidth to fill whatever the parent is - in this case- root

label0 = tk.Label(frame1, text="Select the COM port that the device is plugged in: ")
label0.config(font=("TkDefaultFont", 8))
label0.place(relx = 0.1, rely=0.3, relwidth=0.3, relheight=0.5)


cb = ttk.Combobox(frame1, values=serial_ports())
cb.place(relx=0.5, rely=0.5, anchor='center')
# assign function to cmbobox
cb.bind('<<ComboboxSelected>>', on_select)
# --- frame 1 ---




# --- frame 2 ---
frame2 = tk.Frame(root, bg='#80c1ff') #remove color later
frame2.place(relx=0, rely=0.1, relheight=1, relwidth=1, anchor='nw')

# make a scrollbar
scrollbar = scrolledtext.ScrolledText(frame2)
scrollbar.place(relx=0, rely=0, relheight=1, relwidth=1, anchor='nw')
# --- frame 2 ---
#--------------------------------------------------------------------------------------------------------
t1.start() 

root.mainloop() #here we run our app

When i terminate the GUI that spawns, i get this exception on the terminal:

Exception in thread Thread-1:
Fatal Python error: could not acquire lock for <_io.BufferedWriter name='<stderr
>'> at interpreter shutdown, possibly due to daemon threads
Python runtime state: finalizing (tstate=005C8F38)

Thread 0x00001a30 (most recent call first):
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 1202 in invoke_excepthook
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 934 in _bootstrap_inner
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 890 in _bootstrap

Current thread 0x00001918 (most recent call first):
<no Python frame>

And when i terminate it from the terminal with control+C i get:

Traceback (most recent call last):
  File "mySerial.py", line 110, in <module>
    root.mainloop() #here we run our app
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__i
nit__.py", line 1420, in mainloop
    self.tk.mainloop(n)
KeyboardInterrupt
Exception in thread Thread-1:
Fatal Python error: could not acquire lock for <_io.BufferedWriter name='<stderr
>'> at interpreter shutdown, possibly due to daemon threads
Python runtime state: finalizing (tstate=00578F38)

Thread 0x00001ad0 (most recent call first):
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 1202 in invoke_excepthook
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 934 in _bootstrap_inner
  File "C:\Users\User1\AppData\Local\Programs\Python\Python38-32\lib\threading.p
y", line 890 in _bootstrap

Current thread 0x000013d8 (most recent call first):
<no Python frame>

Why is this hapenning?

EDIT: This is my code and error message, trying AST's advice:

import tkinter as tk
import tkinter.ttk as ttk
import serial.tools.list_ports
from tkinter import scrolledtext 
import time
import serial
import threading
import continuous_threading


#to be used on our canvas
HEIGHT = 700
WIDTH = 800

#hardcoded baud rate
baudRate = 9600

# flag to be notified when application is terminated
stop=False


ser = serial.Serial('COM16', baudRate)
val1 = 0

def readSerial():
    global val1, stop
    if not stop:
       ser_bytes = ser.readline()
       ser_bytes = ser_bytes.decode("utf-8")
       val1 = ser_bytes
       scrollbar.insert("end", val1)
    else:
        return

t1 = continuous_threading.PeriodicThread(0.1, readSerial)
#----------------------------------------------------------------------

# --- functions ---

#the following two functions are for the seria port selection, on frame 1
def serial_ports():    
    return serial.tools.list_ports.comports()

def on_select(event=None):

    global COMPort
    COMPort = cb.get()
    print(COMPort)

def on_close():
    global stop
    stop=True
    root.destroy()



# --- functions ---


#--------------------------------------------------------------------------------
# --- main ---
root = tk.Tk() #here we create our tkinter window
root.title("Sensor Interface")
root.protocol('WM_DELETE_WINDOW', on_close)

#we use canvas as a placeholder, to get our initial screen size (we have defined HEIGHT and WIDTH)
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()

#we use frames to organize all the widgets in the screen
# --- frame 1 ---
frame1 = tk.Frame(root)
frame1.place(relx=0, rely=0.05, relheight=0.03, relwidth=1, anchor='nw') #we use relheight and relwidth to fill whatever the parent is - in this case- root

label0 = tk.Label(frame1, text="Select the COM port that the device is plugged in: ")
label0.config(font=("TkDefaultFont", 8))
label0.place(relx = 0.1, rely=0.3, relwidth=0.3, relheight=0.5)


cb = ttk.Combobox(frame1, values=serial_ports())
cb.place(relx=0.5, rely=0.5, anchor='center')
# assign function to cmbobox
cb.bind('<<ComboboxSelected>>', on_select)
# --- frame 1 ---




# --- frame 2 ---
frame2 = tk.Frame(root, bg='#80c1ff') #remove color later
frame2.place(relx=0, rely=0.1, relheight=1, relwidth=1, anchor='nw')

# make a scrollbar
scrollbar = scrolledtext.ScrolledText(frame2)
scrollbar.place(relx=0, rely=0, relheight=1, relwidth=1, anchor='nw')
# --- frame 2 ---
#--------------------------------------------------------------------------------
t1.start() 
root.mainloop() #here we run our app

The error i get, is the first error i have listed as code.

EDIT 2: When i do the second approach of AST:

If i close the GUI (pressing X), it closes without any errors, but the prompt is stuck on the terminal - i cannot type anything, not even keyboard exit (Control+C) works.

If the program runs and i exit with the keyboard exit in the terminal (Control+C), i get the keyboard interrupt error (the second one i have listed as code)

解决方案

According to me, this occurs because there could be a scheduled thread that gets executed after the application has been destroyed and hence fails to find the GUI element that it has to update.

I haven't tried running your code, but I think this might help

Associate the window deletion with a function that updates the flag (stop)

def on_close():
    global stop
    stop=True
    root.destroy()

root.protocol('WM_DELETE_WINDOW', on_close)
stop=False

And modify the readSerial function to check for the same

def readSerial():
    global val1,stop
    if not stop:
        ser_bytes = ser.readline()
        ser_bytes = ser_bytes.decode("utf-8")
        val1 = ser_bytes
        scrollbar.insert("end", val1)
    else:
        return

EDIT

This is the function in threading.py that raises the exception.

def _bootstrap(self):
    # Wrapper around the real bootstrap code that ignores
    # exceptions during interpreter cleanup.  Those typically
    # happen when a daemon thread wakes up at an unfortunate
    # moment, finds the world around it destroyed, and raises some
    # random exception *** while trying to report the exception in
    # _bootstrap_inner() below ***.  Those random exceptions
    # don't help anybody, and they confuse users, so we suppress
    # them.  We suppress them only when it appears that the world
    # indeed has already been destroyed, so that exceptions in
    # _bootstrap_inner() during normal business hours are properly
    # reported.  Also, we only suppress them for daemonic threads;
    # if a non-daemonic encounters this, something else is wrong.
    try:
        self._bootstrap_inner()
    except:
        if self._daemonic and _sys is None:
            return
        raise

As in this case it's okay to kill the thread when all the non-daemonic threads have been killed, you can try setting the thread as daemonic so that the exception is automatically handled. I don't think the flag would be required with this approach.

t1.daemon=True
t1.start()

这篇关于关闭我的tkinter串行应用程序,给了我一个例外的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆