I am trying to create a GUI using Tkinter, that reads in serial data. I eventually want multiple check buttons (for each possible port), and when the checkbutton is clicked, I would like the raw data from the port to continuously print onto a textbox in the GUI. I did some searching and found that I have to do multiple threads, however, when I do this, the check button is ignored and it automatically prints out the data onto the GUI. Can someone help me?

Here is the code:

from tkinter import *
import tkinter as tk
import serial
import threading
import Queue

class SerialThread(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
    def run(self):
        s = serial.Serial('COM15',4800)
        while True:
            if s.inWaiting():
                text = s.readline(s.inWaiting())
                self.queue.put(text) 

class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.geometry("1360x750")
        frameLabel = tk.Frame(self, padx=40, pady = 40)
        self.text = tk.Text(frameLabel, wrap = 'word', font = 'TimesNewRoman 12', bg = self.cget('bg'), relief = 'flat')
        frameLabel.pack()
        self.text.pack()
        self.queue = Queue.Queue()
        thread = SerialThread(self.queue)
        thread.start()
        self.process_serial() 

    def create_widgets(self):

        Label(self, text = "Choose COM port:").grid(row=0, column = 0, sticky = W)

        #COM 15 check button:
        print("reached check button")
        self.com15 = BooleanVar()
        Checkbutton(self, text = "COM15", variable = self.com15, command = self.process_serial).grid(row=2, column = 0, sticky = W)

        self.result = Text(self, width = 40, height = 40, wrap = WORD)
        self.result.grid(row = 5, column = 0, columnspan = 3) 

    def process_serial(self):
        while self.queue.qsize():
            try:
                self.text.insert('end', self.queue.get())
            except Queue.Empty:
                pass
        self.after(100, self.process_serial)

app = Application()
app.mainloop()

Given that you never actually call create_widgets(), I would expect that it wouldn't display the button in question at all. Similarly, as it is now, it is set up to read from the serial port without regard to the button state (if the button were created in the first place). If you make the following changes, you should get it to do what you want.

import tkinter as tk
import serial
import threading
import queue

class SerialThread(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue.Queue()

    def run(self):
        s = serial.Serial('COM15',4800)
        while True:
            if s.inWaiting():
                text = s.readline(s.inWaiting())
                self.queue.put(text) 


class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.geometry("1360x750")
        self.label = None
        self.ckbutton = None
        self.text = None
        frameLabel = tk.Frame(self, padx=40, pady = 40)
        frameLabel.pack()
        self.text.pack()
        self.create_widgets()
        self.queue = Queue.Queue()
        thread = SerialThread(self.queue)
        thread.start()
        self.process_serial() 


    def create_widgets(self):
        self.label = Label(self, text = "Choose COM port:").grid(row=0, column = 0, sticky = W)
        #COM 15 check button:
        print("reached check button")
        self.com15 = BooleanVar()
        self.ckbutton = tk.Checkbutton(self, text = "COM15", variable = self.com15, command = self.process_serial).grid(row=2, column = 0, sticky = W)

        self.text = tk.Text(self, width = 40, height = 40, wrap = WORD)
        self.text.grid(row = 5, column = 0, columnspan = 3) 

    def process_serial(self):
        while self.queue.qsize():
            try:
                self.text.insert('end', self.queue.get())
            except Queue.Empty:
                pass
        self.after(100, self.process_serial)

app = Application()
app.mainloop()

Edited 1 Year Ago by Schol-R-LEA

And threads may be overkill here. You could call one function periodically (Tkinter's after method) that does or does not read each port depending on the value of a variable(checkbutton_variable.get) associated with that port.

And I didn't post that if you are still having problems, I can post some sample code on how to do this. But I use multiprocessing, not threading.

This article has been dead for over six months. Start a new discussion instead.