如何在具有多个按钮的文本小部件内容之间切换?

问题描述 投票:0回答:1

我想让应用程序从 FTP 服务器读取日志文件 - 当用户单击按钮时,它会连接到 FTP,更改目录并每 1 秒读取数据。如果数据发生更改(出现新日志),则会将其添加到文本小部件。 它工作得很酷,直到用户多次按下同一按钮 - 然后出现错误“运行时错误:线程只能启动一次”。因此,在下面这段代码的不同版本中,我为每个 USB 按钮添加了附加按钮“显示日志”。此外,每个 USB 按钮都有自己的文本小部件。 因此,当用户首先按下 USB 按钮时,他可以通过“显示日志”按钮在文本小部件之间切换 - 但它变得混乱,主要是因为我在告诉我的 func readdata() 哪个文本小部件忘记(隐藏)以及哪个渲染时遇到问题(再次.pack)-例如,我想随后 pack_forget Text widget1 和 .pack Text widget2,但我不知道如何让应用程序记住活动上下文。此外,滚动在“切换到”文本小部件中不起作用(仅在“活动”文本中起作用),有时无法读取数据文件和许多其他问题。 这是更优雅的方式吗?我的意思是,只需单击“USB0”和“USB1”按钮即可在日志之间切换,而不需要为每个 USB 按钮使用额外的“显示日志”按钮?

import tkinter as tk
import pysftp
import time
import threading

root = tk.Tk()
root.geometry("1200x700")
frame = tk.Frame(root)
frame.place(x=15, y=10)
scroll = tk.Scrollbar(frame)
t = tk.Text(frame, width=183, height=45, yscrollcommand=scroll.set)
scroll.config(command=t.yview)
scroll.pack(side='right', fill='y')

def clears():
    t.delete(1.0, tk.END)

def readdata(text,dir,logfile):
    clears()
    t.insert(tk.END, text+"LOADING LOGS\n\n")
    cnopts = pysftp.CnOpts()
    cnopts.hostkeys = None
    sftp = pysftp.Connection('ip', username='user', password='pass', cnopts=cnopts)
    sftp.chdir('uart-logs')
    sftp.chdir(dir) 
    active_log = logfile 
    with sftp.open(active_log, mode="r") as file:
        old_text = file.read().decode('ASCII')
        t.insert(tk.END, old_text)
        t.see('end')
    while True:
        with sftp.open(active_log, mode="r") as file:
            new_text = file.read().decode('ASCII')
        if new_text != old_text:
            t.insert(tk.END, new_text[len(old_text):])
            root.update()
            t.see('end')
            old_text = new_text
        time.sleep(1)

buttons = tk.Frame()
b0 = tk.Button(buttons, text = "ttyUSB0", command =threading.Thread(target=lambda:readdata('USB0: ','USB0',"USB0-active.log")).start)
b1 = tk.Button(buttons, text = "ttyUSB1", command =threading.Thread(target=lambda:readdata('USB1: ','USB1',"USB1-active.log")).start)
clear = tk.Button(buttons, text="Clear", command=lambda:clears())
chkbox = tk.Checkbutton(buttons, text="Auto-scroll")

buttons.pack()
b0.pack(in_=buttons, side=tk.LEFT, padx=10)
b1.pack(in_=buttons, side=tk.LEFT, padx=10)
clear.pack(in_=buttons, side=tk.LEFT, padx=10)
chkbox.pack(in_=buttons, side=tk.RIGHT, padx=40)

frame.pack(fill='both',expand=True)
t.pack(fill='both',expand=True)

tk.mainloop()
python python-3.x tkinter python-multithreading
1个回答
2
投票

它工作得很酷,直到用户多次按下同一按钮 - 然后出现错误“运行时错误:线程只能启动一次”。

只需更改即可解决问题

command=threading.Thread(target=lambda:readdata('USB0: ','USB0',"USB0-active.log")).start

(将单个线程的

start
方法绑定到按钮) 到

command=lambda: threading.Thread(target=lambda:readdata('USB0: ','USB0',"USB0-active.log")).start()

这将创建一个新线程并在每次单击它时启动它, 但您还需要能够阻止前一个线程同时尝试更新该字段。

你可以用类似的东西来做到这一点

current_read_thread = None
current_stop_event = None


def readdata(text, dir, logfile, stop_event):
    ...
    while not stop_event.is_set():  # (not `while True`)
        ...


def start_thread(text, dir, logfile):
    global current_read_thread
    global current_stop_event
    if current_read_thread:
        current_stop_event.set()
        # Wait for the other thread to stop
        current_read_thread.join()
        current_read_thread = None
    current_stop_event = threading.Event()
    current_read_thread = threading.Thread(target=readdata, args=(text, dir, logfile, current_stop_event))
    current_read_thread.start()


# ...

b0 = tk.Button(buttons, text="ttyUSB0", command=lambda: start_thread("USB0: ", "USB0", "USB0-active.log"))
© www.soinside.com 2019 - 2024. All rights reserved.