Tkinter:对每秒发生的事件进行多处理
Tkinter: multiprocessing on events that happen per second
我制作了一个从 modbus 从属设备读取的 Tkinter 程序。它每秒读取设备并将输出显示到标签上。但是,我有多个选项卡 运行 每个连接的设备都有相同的代码。在读取设备时,整个 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()
总体思路是将从传感器读取的代码放在线程中,并让该代码通过队列与 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()
我制作了一个从 modbus 从属设备读取的 Tkinter 程序。它每秒读取设备并将输出显示到标签上。但是,我有多个选项卡 运行 每个连接的设备都有相同的代码。在读取设备时,整个 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()
总体思路是将从传感器读取的代码放在线程中,并让该代码通过队列与 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()