Tkinter:对每秒发生的事件进行多重处理

2024-04-26 06:04:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我做了一个Tkinter程序,从modbus从设备读取数据。它每秒读取设备并将输出显示在标签上。但是,我有多个选项卡,它们为每个连接的设备运行相同的代码。当一个设备正在被读取时,整个GUI都会冻结,所以在读取完成之前,您不能移动程序或按下按钮。多处理有助于冻结吗?如果是这样的话,我该如何实施呢?在

这是我的代码:

import tkinter as tk
from tkinter import *
from tkinter import ttk
from time import time
import minimalmodbus
import serial
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True

class Page(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
    def show(self):
        self.lift()

class Page1(Page):
    def __init__(self, *args, **kwargs):
       Page.__init__(self, *args, **kwargs)

       self.gas = minimalmodbus.Instrument('COM3', 1)
       self.gas.serial.baudrate = 9600
       self.gas.serial.bytesize = 8
       self.gas.serial.parity = serial.PARITY_NONE
       self.gas.serial.stopbits = 1
       self.gas.serial.timeout = 0.25
       self.gas.mode = minimalmodbus.MODE_RTU

       self.value_display = tk.Label(self, text='value', width=10)
       self.value_display.pack(side="top")

       self.unit_display = tk.Label(self, text='unit', width=10)
       self.unit_display.pack(side="top")

       self.gas_display = tk.Label(self, text='temp', width=10)
       self.gas_display.pack(side="top")

       self.status_display = tk.Label(self, text='status', width=10)
       self.status_display.pack(side="top")

       self.command_display = tk.Label(self, text='command', width=10)
       self.command_display.pack(side="top")

       self.pressure_display = tk.Label(self, text='pressure', width=10)
       self.pressure_display.pack(side="top")

       self.timer_button = tk.Button(self, text='Start', command=self.toggle)
       self.timer_display = tk.Label(self, text='00:00', width=10)
       self.timer_button.pack(side="top")
       self.timer_display.pack(side="top")
       self.paused = True

    def gas_meth(self):
        try:
            gas_value = self.gas.read_registers(0,42)

            self.value_display.config(text=gas_value[0])
            self.unit_display.config(text=gas_value[1])
            self.gas_display.config(text=gas_value[2])
            self.status_display.config(text=gas_value[3])
            self.command_display.config(text=gas_value[4])
            self.pressure_display.config(text=gas_value[5])

        except IOError:
            self.gas_display.config(text="Lost con.")
        except ValueError:
            self.gas_display.config(text="RTU error")
        self.gas_display.after(1000, self.gas_meth)

    def toggle(self):
        if self.paused:
            self.paused = False
            self.timer_button.config(text='Stop')
            self.oldtime = time()
            self.run_timer()
            self.gas_meth()
        else:
            self.paused = True
            self.oldtime = time()
            self.timer_button.config(text='Start')

    def run_timer(self):
        if self.paused:
            return
        delta = int(time() - self.oldtime)
        timestr = '{:02}:{:02}'.format(*divmod(delta, 60))
        self.timer_display.config(text=timestr)
        self.timer_display.after(500, self.run_timer)

class MainView(tk.Frame):
    def __init__(self, *args, **kwargs):
        tk.Frame.__init__(self, *args, **kwargs)
        p1 = Page1(self)

        buttonframe = tk.Frame(self)
        container = tk.Frame(self)
        buttonframe.pack(side="top", fill="x", expand=False)
        container.pack(side="top", fill="both", expand=True)

        p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
        b1 = tk.Button(buttonframe, text="Page 1", command=p1.lift)
        b1.pack(side="left")
        p1.show()

if __name__ == "__main__":
    root = tk.Tk()
    main = MainView(root)
    main.pack(side="top", fill="both", expand=True)
    root.wm_geometry("1000x600")
    root.mainloop()

Tags: textselfconfigvaluetopdefdisplayserial
1条回答
网友
1楼 · 发布于 2024-04-26 06:04:58

一般的想法是将从传感器读取的代码放在一个线程中,并让该代码通过队列与GUI线程通信。在

下面是一个非常快速的技巧演示:

import tkinter as tk
import threading
import queue
import random
import time

class Example(object):
    def __init__(self):
        self.root = tk.Tk()
        self.sensor_vars = []

        self.root.grid_columnconfigure(1, weight=1)
        for row, i in enumerate(range(3)):
            var = tk.StringVar()
            self.sensor_vars.append(var)
            label = tk.Label(self.root, text="Sensor %d:" % i)
            value = tk.Label(self.root, textvariable=var, width=4)
            label.grid(row=row, column=0, sticky="e")
            value.grid(row=row, column=1, sticky="w")

        # create a queue for communication
        self.queue = queue.Queue()

        # create some sensors
        self.sensors = []
        for i in range(3):
            sensor = Sensor(self.queue, i)
            self.sensors.append(sensor)
            sensor.setName("Sensor %d" % i)

        # start polling the queue
        self.poll_queue()

    def start(self):
        # start the sensors
        for sensor in self.sensors:
            sensor.start()

        # start the GUI loop
        self.root.mainloop()

        # wait for the threads to finish
        for sensor in self.sensors:
            sensor.stop()
            sensor.join()

    def poll_queue(self):
        if not self.queue.empty():
            message = self.queue.get()
            index = message["index"]
            self.sensor_vars[index].set(message["value"])

        self.root.after(100, self.poll_queue)

class Sensor(threading.Thread):
    def __init__(self, queue, index):
        threading.Thread.__init__(self)
        self.queue = queue
        self.index = index
        self.stop_requested = False

    def stop(self):
        self.stop_requested = True

    def run(self):
        for i in range(10):
            if self.stop_requested:
                break
            value = random.randint(10, 100)
            self.queue.put({"index": self.index, "value": value})
            time.sleep(1)



if __name__ == "__main__":
    app = Example()
    app.start()

相关问题 更多 >