Программа вылетает при вызове меню при использовании потоковой передачи и StringVar () - PullRequest
0 голосов
/ 06 августа 2020

Я сделал программу Python, которая читает файл, а затем отправляет / принимает данные в / из микроконтроллера, и все работало хорошо, пока я не добавил меню для отображения коротких инструкций.

Поскольку связь UART должна выполняться в отдельном потоке, я использовал threading и StringVar() для доступа к данным из основного потока.

Чтобы продемонстрировать проблему, я сделал небольшой пример , не имеющий ничего общего с микроконтроллерами .

Шаги для воспроизведения проблемы:

  1. Щелкните переключатель Next screen, чтобы открыть второй экран (на на начальном экране меню работает хорошо)
  2. Щелкните Help > Instructions

После (или иногда даже до) закрытия окна сообщения программа выдаст sh с:

TclStackFree: incorrect freePtr. Call out of sequence?

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Примечание: В исходной программе, где есть больше элементов пользовательского интерфейса, программа всегда дает сбой перед отображением окна сообщения, а после удаления еще большего количества элементов пользовательского интерфейса программа не ломает sh каждый раз - вот почему я оставил несколько «лишних» меток. Когда добавляются еще несколько меток, программа каждый раз будет cra sh.

Я сузил причину cra sh до:

displayVal.set()

в функции checkForData(). После удаления этой инструкции все работает нормально.

Более того, после удаления displayVal.trace("w", displayVal_trace) программа больше не будет трещать sh, но открытие меню все равно временно заморозит рабочий поток.

Я думал после displayVal.set() Tkinter пытается обновить метку, но не может из-за отображения меню - однако проблема осталась даже после того, как я удалил label_data = Label(up2, textvariable = displayVal).

Вот урезанный код, протестированный с Python 2,7:

from Tkinter import *
import tkMessageBox
import threading
import time

threadRun = True
checkDelay = 0.5

def checkForData():
    global threadRun, checkDelay
    print "Simulating thread for receiving messages from UART"
    while threadRun == True:
        print time.time()
        displayVal.set(time.time())     # <-- if removed the menu works OK (no crash)
        time.sleep(checkDelay)
    print "No more receiving of messages"

def listenForData():
    t = threading.Thread(target=checkForData)
    t.daemon = False
    t.start()

def stopListening():
    global threadRun, checkDelay
    threadRun = False
    time.sleep(checkDelay + 0.1)

def exit_handler():
    print "Exiting..."
    stopListening()
    root.destroy()

root = Tk()
right = int((root.winfo_screenwidth() - 600) / 2)
down = int(root.winfo_screenheight() / 3 - 400 / 2)
root.geometry("600x400+%d+%d" % (right, down))
root.resizable(width = False, height = False)
root.protocol("WM_DELETE_WINDOW", exit_handler)

displayVal = StringVar()
displayVal.set("nothing")

def setupView():
    global masterframe
    masterframe = Frame()
    masterframe.pack(fill=BOTH, expand=True)
    selectPort()    # the 1st screen for selecting COM ports

def selectPort():
    global masterframe, radioVar
    # remove everything from the frame
    for child in masterframe.winfo_children():
        child.destroy()
    radioVar = StringVar()

    l1 = Label(masterframe, text = "Select...")
    l1.pack(pady=(50, 20))

    # this would be a list of detected COM ports
    lst = ["Next screen"]

    if len(lst) > 0:
        for n in lst:
            r1 = Radiobutton(masterframe, text=n, variable=radioVar, value=n)
            r1.config(command = next_screen)
            r1.pack()

def mainScreen():
    global masterframe, term, status

    # remove previous screen from the frame
    for child in masterframe.winfo_children():
        child.destroy()
    
    up1 = Frame(masterframe)
    up1.pack(side=TOP)

    up2 = Frame(masterframe)
    up2.pack()

    terminal = Frame(masterframe)
    terminal.pack()

    down = Frame(masterframe)
    down.pack(side=BOTTOM, fill=BOTH)

    label_top = Label(up1, text="Something")
    label_top.pack(pady=5)

    label_data = Label(up2, textvariable = displayVal)
    label_data.pack(pady=(10, 0))

    term = Text(terminal, height=10, width=35, bg="white")
    term.pack()
    term.tag_config("red", foreground="red")
    term.tag_config("blue", foreground="blue")
    term.insert(END, "The file has been read\n", "red")
    term.insert(END, "File contents:\n")
    term.insert(END, data)

    status = Label(down, text="Status...", bd=1, relief=SUNKEN, anchor=W, bg="green")
    status.pack(fill=X)

    displayVal.trace("w", displayVal_trace)     # <-- if removed only temporary freeze but no crash

def displayVal_trace(name, index, mode):
    global term

    if(displayVal.get() != "NOTHING"):
        term.insert(END, "\nReceived: ", "blue")
        term.insert(END, displayVal.get())
        term.see(END)

def next_screen():
    listenForData()
    mainScreen()

def stop():
    stopListening()

def instructions():
    tkMessageBox.showinfo("Help", "This is help")

main_menu = Menu(root)
root.config(menu = main_menu)
help_menu = Menu(main_menu)
main_menu.add_cascade(label="Help", menu=help_menu)
help_menu.add_command(label="Instructions", command=instructions)

data = [[1], [2], [3]]      # this would be the data read from the file
b1 = data[0][0]
b2 = data[1][0]
b3 = data[2][0]
print "Read from file:", b1, b2, b3

setupView()
root.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...