在 python 中定期更新 gtk 的列表存储
updating the list store of gtk periodically in python
我是phyton编程新手,开发的gui界面有gtk框架和串口。它有一个 treeview 的 liststore 模型。我可以轻松地插入新行。
我在与主 gui 线程不同的线程中使用 serialport recive 回调,避免错过任何数据。收到新数据后,它应该更新树视图。但是,由于串口在不同的线程中,我不知道如何更新列表。请帮我做一下。
图形界面 class:
class MainGUI():
def __init__(self):
self.builder = Gtk.Builder()
self.builder.add_from_file("main.glade")
self.builder.connect_signals(MainGUI)
self.window = self.builder.get_object("window1")
self.mycombobox = self.builder.get_object('comboboxtext1')
self.toggle = self.builder.get_object('togglebutton1')
self.table = self.builder.get_object('treeview2')
self.list = self.builder.get_object('liststore1')
self.scroll_window = self.builder.get_object('scrolledwindow1')
def show(self):
print("App main thread number", format(threading.get_ident()))
self.window.show()
Gtk.main()
@staticmethod
def connect_toggled(_self):
if main.toggle.get_active():
main.toggle.set_label("Disconnect")
serial_port.connect(main.mycombobox.get_active_text())
t3 = threading.Thread(target=serial_port.read_from_port)
t3.start()
serial_port.disconnect()
def row_inserted_event(self, path, iter):
"""The actual scrolling method"""
adj = main.scroll_window.get_vadjustment()
adj.set_value(adj.get_upper() - adj.get_page_size())
def update_table(self):
# for i in range(256):
# main.list.append(['aaa', 'ddds', i])
# if len(main.list) > 50:
# main.list.remove(main.list.get_iter(0))
main.list.append(['aaa', 'ddds', 0])
if len(main.list) > 50:
main.list.remove(main.list.get_iter(0))
print(len(main.list))
if __name__ == "__main__":
serial_port = SerialPort()
ports = SerialPort().list_ports()
main = MainGUI()
for port in ports:
main.mycombobox.append_text(port)
main.mycombobox.set_active(0)
main.toggle.set_label("Connect")
main.update_table()
main.show()
串口class:
class SerialPort:
def __init__(self):
self.ser = serial.Serial()
self.baud_rate = 115200
def write(self, data):
self.ser.write(bytes(data))
print(data)
def connect(self, port):
print("serial port thread number = %d" % (threading.get_ident()))
print("connected the port = %s" % (port))
self.ser.port = port
self.ser.baudrate = self.baud_rate
self.ser.timeout = 0
if self.ser.isOpen():
print("already connected this port = %s" % (port))
else:
self.ser.open()
def disconnect(self):
if self.ser.isOpen():
self.ser.close()
print("disconnected port")
def read_from_port(self):
while True:
if self.ser.isOpen():
reading = self.ser.readline()
if len(reading) > 0:
self.received_callback(reading)
time.sleep(0.1)
def received_callback(self, data):
print(data)
def list_ports(self):
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
# ports = glob.glob('/dev/tty[A-Za-z]*')
ports = ['/dev/pts/%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
我认为您的问题更多地与线程 + GUI 有关,而不是 GTK。
据我所知,当您修改 treeview
的模型 liststore
时,后者应该立即更新。所以,那里应该没有问题。
使用线程和 GUI 时的一个基本原则是,您应该只从其自己的线程(主循环)内更新 GUI。所以你需要做的是让你的工作线程(串行端口连接线程)将更新发送到主 GUI 线程并让它更新 treeview
。可以使用 GLib.idle_add
函数安排更新,让 GTK 在最方便的时候进行。
现在,要在线程之间进行通信,您可以使用 queue
模块。
我不太明白你的代码。所以我会写一个简单的例子(使用 gtk3 PyGObject,因为你没有指定)。
import threading
import queue
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GLib', '2.0')
from gi.repository import Gtk, GLib
def do_work(com_queue):
# do some work
com_queue.put("update for your treeview")
# continue
class MainGUI(object):
def __init__(self):
self.com_queue = queue.Queue()
self.worker_thread = None
self.liststore = None
# more gui initialization...
def launch_worker_thread(self):
self.worker_thread = threading.Thread(target=do_work, args=(self.com_queue,))
self.worker_thread.start()
Glib.timeout_add(1000, self.check_queue) # run check_queue every 1 second
def check_queue(self):
if self.worker_thread.is_alive():
try:
update = self.com_queue.get()
GLib.idle_add(self.update_treeview, (update,)) # send tuple
except queue.Empty:
pass
return True # to keep timeout running
else:
return False # to end timeout
def update_treeview(self, update):
self.liststore.append(update) # here update the treeview model with tuple
if __name__ == "__main__":
gui = MainGUI()
Gtk.main()
希望对您有所帮助。
我是phyton编程新手,开发的gui界面有gtk框架和串口。它有一个 treeview 的 liststore 模型。我可以轻松地插入新行。 我在与主 gui 线程不同的线程中使用 serialport recive 回调,避免错过任何数据。收到新数据后,它应该更新树视图。但是,由于串口在不同的线程中,我不知道如何更新列表。请帮我做一下。
图形界面 class:
class MainGUI():
def __init__(self):
self.builder = Gtk.Builder()
self.builder.add_from_file("main.glade")
self.builder.connect_signals(MainGUI)
self.window = self.builder.get_object("window1")
self.mycombobox = self.builder.get_object('comboboxtext1')
self.toggle = self.builder.get_object('togglebutton1')
self.table = self.builder.get_object('treeview2')
self.list = self.builder.get_object('liststore1')
self.scroll_window = self.builder.get_object('scrolledwindow1')
def show(self):
print("App main thread number", format(threading.get_ident()))
self.window.show()
Gtk.main()
@staticmethod
def connect_toggled(_self):
if main.toggle.get_active():
main.toggle.set_label("Disconnect")
serial_port.connect(main.mycombobox.get_active_text())
t3 = threading.Thread(target=serial_port.read_from_port)
t3.start()
serial_port.disconnect()
def row_inserted_event(self, path, iter):
"""The actual scrolling method"""
adj = main.scroll_window.get_vadjustment()
adj.set_value(adj.get_upper() - adj.get_page_size())
def update_table(self):
# for i in range(256):
# main.list.append(['aaa', 'ddds', i])
# if len(main.list) > 50:
# main.list.remove(main.list.get_iter(0))
main.list.append(['aaa', 'ddds', 0])
if len(main.list) > 50:
main.list.remove(main.list.get_iter(0))
print(len(main.list))
if __name__ == "__main__":
serial_port = SerialPort()
ports = SerialPort().list_ports()
main = MainGUI()
for port in ports:
main.mycombobox.append_text(port)
main.mycombobox.set_active(0)
main.toggle.set_label("Connect")
main.update_table()
main.show()
串口class:
class SerialPort:
def __init__(self):
self.ser = serial.Serial()
self.baud_rate = 115200
def write(self, data):
self.ser.write(bytes(data))
print(data)
def connect(self, port):
print("serial port thread number = %d" % (threading.get_ident()))
print("connected the port = %s" % (port))
self.ser.port = port
self.ser.baudrate = self.baud_rate
self.ser.timeout = 0
if self.ser.isOpen():
print("already connected this port = %s" % (port))
else:
self.ser.open()
def disconnect(self):
if self.ser.isOpen():
self.ser.close()
print("disconnected port")
def read_from_port(self):
while True:
if self.ser.isOpen():
reading = self.ser.readline()
if len(reading) > 0:
self.received_callback(reading)
time.sleep(0.1)
def received_callback(self, data):
print(data)
def list_ports(self):
if sys.platform.startswith('win'):
ports = ['COM%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
# ports = glob.glob('/dev/tty[A-Za-z]*')
ports = ['/dev/pts/%s' % (i + 1) for i in range(256)]
elif sys.platform.startswith('darwin'):
ports = glob.glob('/dev/tty.*')
else:
raise EnvironmentError('Unsupported platform')
result = []
for port in ports:
try:
s = serial.Serial(port)
s.close()
result.append(port)
except (OSError, serial.SerialException):
pass
return result
我认为您的问题更多地与线程 + GUI 有关,而不是 GTK。
据我所知,当您修改 treeview
的模型 liststore
时,后者应该立即更新。所以,那里应该没有问题。
使用线程和 GUI 时的一个基本原则是,您应该只从其自己的线程(主循环)内更新 GUI。所以你需要做的是让你的工作线程(串行端口连接线程)将更新发送到主 GUI 线程并让它更新 treeview
。可以使用 GLib.idle_add
函数安排更新,让 GTK 在最方便的时候进行。
现在,要在线程之间进行通信,您可以使用 queue
模块。
我不太明白你的代码。所以我会写一个简单的例子(使用 gtk3 PyGObject,因为你没有指定)。
import threading
import queue
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('GLib', '2.0')
from gi.repository import Gtk, GLib
def do_work(com_queue):
# do some work
com_queue.put("update for your treeview")
# continue
class MainGUI(object):
def __init__(self):
self.com_queue = queue.Queue()
self.worker_thread = None
self.liststore = None
# more gui initialization...
def launch_worker_thread(self):
self.worker_thread = threading.Thread(target=do_work, args=(self.com_queue,))
self.worker_thread.start()
Glib.timeout_add(1000, self.check_queue) # run check_queue every 1 second
def check_queue(self):
if self.worker_thread.is_alive():
try:
update = self.com_queue.get()
GLib.idle_add(self.update_treeview, (update,)) # send tuple
except queue.Empty:
pass
return True # to keep timeout running
else:
return False # to end timeout
def update_treeview(self, update):
self.liststore.append(update) # here update the treeview model with tuple
if __name__ == "__main__":
gui = MainGUI()
Gtk.main()
希望对您有所帮助。