Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 Tkinter GUI is unresponsive in a thread
#1
Hi everyone,

My code is about a graph based on two values which are input through a serial port and a linear graph is the output. But the issue I'm facing is that there are 2 GUI interface one shows the values being sent and the other shows the graph output. So when the program runs the data window is running fine but the graph window becomes unresponsive although the values are being generated.
The code is distributed among two files: SMSRRTT2.0.py and e8.py Here's the code for the respective file.

SMSRRTT2.0.py
"""This is a simple application written in Python and TKinter.

The application's main purpose is not to serve a specific one. This is a generic application
for sending and receiving data from the computer to UART host controller (Arduino).

The major functions are self update and get data which are threaded to make sure the GUI does not freeze.
The GUI runs in the main thread, the worker threads are the two separate ones.

A simple Arduino Test Sketch is also made to test the purpose of this app which sends data in a specific 
format. 

Format: 
    String outgoing_data = {"1,2,3,4,5"};

The array size has been limited to 5 since the UNO has 5 analogue Sources and the progress bars 
represent the 5 bars.


"""


import time
import threading
import tkinter
from tkinter import ttk
from tkinter import *
import serial
import csv
import os
import datetime
import matplotlib

serial_data = ''
filter_data = ''
update_period = 5
serial_object = None
gui = Tk()
gui.title("UART Interface")

data4 = ''
data5 = ''






def connect():
    """The function initiates the Connection to the UART device with the Port and Buad fed through the Entry
    boxes in the application.

    The radio button selects the platform, as the serial object has different key phrases 
    for Linux and Windows. Some Exceptions have been made to prevent the app from crashing,
    such as blank entry fields and value errors, this is due to the state-less-ness of the 
    UART device, the device sends data at regular intervals irrespective of the master's state.

    The other Parts are self explanatory.
    """

    version_ = button_var.get()
    print(version_)
    global serial_object
    port = port_entry.get()
    baud = baud_entry.get()
    

    try:
        if version_ == 2:
            try:
                serial_object = serial.Serial('/dev/tty' + str(port), baud)
            
            except:
                print ("Cant Open Specified Port")
        elif version_ == 1:
            serial_object = serial.Serial('COM' + str(port), baud)

    except ValueError:
        print ("Enter Baud and Port")
        return

    t1 = threading.Thread(target = get_data)
    t1.daemon = True
    t1.start()
    


def get_data():
    """This function serves the purpose of collecting data from the serial object and storing 
    the filtered data into a global variable.

    The function has been put into a thread since the serial event is a blocking function.
    """
    global serial_object
    global filter_data

    while(1):   
        try:
            serial_data = str(serial_object.readline().decode("ascii")) 
            filter_data = serial_data.split(',')
            graph_data = filter_data[0] 
            gd = int(graph_data)
            graph_data1 = filter_data[1] 
            gd1 = int(graph_data1)
            gd2 = filter_data[2]

            
            with open("file.txt", 'a') as f:
                
                    f.write('%d'%gd + ',' + '%d'%gd1 + ',' + gd2 +'\n')
            print(filter_data)
            print(type(filter_data))
        except TypeError:
            pass
    
    
        

def update_gui():
    """" This function is an update function which is also threaded. The function assimilates the data
    and applies it to it corresponding progress bar. The text box is also updated every couple of seconds.

    A simple auto refresh function .after() could have been used, this has been avoid purposely due to 
    various performance issues.


    """
    global filter_data
    global update_period

    text.place(x = 15, y = 10)
    progress_1.place(x = 60, y = 100)
    progress_2.place(x = 60, y = 130)
    progress_3.place(x = 60, y = 160)
    progress_4.place(x = 60, y = 190)
    progress_5.place(x = 60, y = 220)
    new = time.time()
    
    while(1):
        if filter_data:

            text.insert(END, filter_data)
            
            try:
                progress_1["value"] = filter_data[0]
                progress_2["value"] = filter_data[1]
                progress_3["value"] = filter_data[2]
                progress_4["value"] = filter_data[3]
                progress_5["value"] = filter_data[4]

            
            except :
                pass

            
            if time.time() - new >= update_period:
                text.delete("1.0", END)
                progress_1["value"] = 0
                progress_2["value"] = 0
                progress_3["value"] = 0
                progress_4["value"] = 0
                progress_5["value"] = 0
                new = time.time()


def grh():
    os.system("e8.py")
 




def send():
    """This function is for sending data from the computer to the host controller.
    

        The value entered in the the entry box is pushed to the UART. The data can be of any format, since
        the data is always converted into ASCII, the receiving device has to convert the data into the required f
        format.
    """
    send_data = data_entry.get()
    
    if not send_data:
        print("Sent Nothing")
    
    serial_object.write(send_data.encode())



def disconnect():
    """ 
    This function is for disconnecting and quitting the application.

    Sometimes the application throws a couple of errors while it is being shut down, the fix isn't out yet
    but will be pushed to the repo once done.

    simple GUI.quit() calls.

    """
    try:
        serial_object.close() 
    
    except AttributeError:
        print("Closed without Using it -_-")

    gui.quit()



if __name__ == "__main__":

    """
    The main loop consists of all the GUI objects and its placement.

    The Main loop handles all the widget placements.

    """
    #frames
    frame_1 = Frame(height = 285, width = 480, bd = 3, relief = 'groove').place(x = 7, y = 5)
    frame_2 = Frame(height = 150, width = 480, bd = 3, relief = 'groove').place(x = 7, y = 300)
    text = Text(width = 55, height = 5)




    #threads
    t2 = threading.Thread(target = update_gui)
    t2.daemon = True
    t2.start()

    
    #Labels
    data1_ = Label(text = "Data1:").place(x = 15, y= 100)
    data2_ = Label(text = "Data2:").place(x = 15, y= 130)
    data3_ = Label(text = "Data3:").place(x = 15, y= 160)
    data4_ = Label(text = "Data4:").place(x = 15, y= 190)
    data5_ = Label(text = "Data5:").place(x = 15, y= 220)

    baud   = Label(text = "Baud").place(x = 100, y = 348)
    port   = Label(text = "Port").place(x = 200, y = 348)
    contact = Label(text = "SAFE MINETECH").place(x = 250, y = 437)

    #progress_bars
    progress_1 = ttk.Progressbar(orient = HORIZONTAL, mode = 'determinate', length = 200, max = 255)
    progress_2 = ttk.Progressbar(orient = HORIZONTAL, mode = 'determinate', length = 200, max = 255)
    progress_3 = ttk.Progressbar(orient = HORIZONTAL, mode = 'determinate', length = 200, max = 255)
    progress_4 = ttk.Progressbar(orient = HORIZONTAL, mode = 'determinate', length = 200, max = 255)
    progress_5 = ttk.Progressbar(orient = HORIZONTAL, mode = 'determinate', length = 200, max = 255)



    #Entry
    data_entry = Entry()
    data_entry.place(x = 100, y = 255)
    
    baud_entry = Entry(width = 7)
    baud_entry.place(x = 100, y = 365)
    
    port_entry = Entry(width = 7)
    port_entry.place(x = 200, y = 365)



    #radio button
    button_var = IntVar()
    radio_1 = Radiobutton(text = "Windows", variable = button_var, value = 1).place(x = 10, y = 315)
    radio_2 = Radiobutton(text = "Linux", variable = button_var, value = 2).place(x = 110, y = 315)

    #button
    button1 = Button(text = "Send", command = send, width = 6).place(x = 15, y = 250)
    connect = Button(text = "Connect", command = connect).place(x = 15, y = 360)
    disconnect = Button(text = "Disconnect", command = disconnect).place(x =300, y = 360)
    save = Button(text = "Save", command = disconnect).place(x =415, y = 250)
    graph = Button(text = "Graph", command = grh).place(x =415, y = 100)
    #mainloop
    gui.geometry('500x500')
    #ani = animation.FuncAnimation(f, animate, fargs=(xs, ys, ys1), interval=800)
    gui.mainloop() 
e8.py
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.animation as animation
from matplotlib import style
import datetime as dt

import tkinter as tk
from tkinter import ttk
import matplotlib.pyplot as plt

from tkinter import *
import pandas as pd

from tkinter import Frame, Tk, BOTH, Text, Menu, END
from tkinter import filedialog 
from matplotlib import pyplot as plt
import os
NORM_FONT= ("Verdana", 10)
LARGE_FONT= ("Verdana", 12)
style.use("fast")

f = plt.Figure(figsize=(5,5), dpi=100)
ax = f.add_subplot(231)

ax1 = f.add_subplot(232)
ax2 = f.add_subplot(233)
ax3 = f.add_subplot(234)
ax4 = f.add_subplot(235)
ax5 = f.add_subplot(236)

xs = []
ys = []
ys1 = []
ys2 = []
ys3 = []
ys4 = []
ys5 = []


def animate(i,xs,ys,ys1):


    
    graph_data = open('file.txt','r').read()
    lines = graph_data.split('\n')

    for line in lines:
        if len(line) > 1:
            x, x1, x2 = line.split(',')   

            #tm = dt.datetime.now().strftime('%S')

    # Add x and y to lists
            xs.append(float(x))
            ys.append(x2)
            ys1.append(float(x1))
            #ys1.append(float(x1))
            #ys2.append(float(x2))
            #ys3.append(float(x3))
            #ys4.append(float(x4))
            #ys5.append(float(x5))

    # Limit x and y lists to 20 items
    xs = xs[-10:]
    ys = ys[-10:]
    ys1 = ys1[-10:]
    #ys1 = ys1[-20:]
    #ys2 = ys2[-20:]
    #ys3 = ys3[-20:]
    #ys4 = ys4[-20:]
    #ys5 = ys5[-20:]
    
    # Draw x and y lists

    ax.clear()
    ax1.clear()
    ax2.clear()
    ax3.clear()
    ax4.clear()
    ax5.clear()
    ax.set_xlabel('Time')
    ax.set_ylabel('sensor1')

    ax1.set_xlabel('Time')
    ax1.set_ylabel('sensor2')

    ax2.set_xlabel('Time')
    ax2.set_ylabel('sensor3')

    ax3.set_xlabel('Time')
    ax3.set_ylabel('sensor4')

    ax4.set_xlabel('Time')
    ax4.set_ylabel('sensor5')

    ax5.set_xlabel('Time')
    ax5.set_ylabel('sensor6')
    ax.plot(xs,ys)
    ax1.plot(ys1,ys)
    #ax2.plot(xs,ys2)
    #ax3.plot(xs,ys3)
    #ax4.plot(xs,ys4)
    #ax5.plot(xs,ys5)
    
    # Format plot


    #ax.plot_date(str(rs), "#00A3E0", label="value")
    #a.plot_date(sellDates, sells["price"], "#183A54", label="sells")

    #handles, labels = ax.get_legend_handles_labels(str(rs))
    #ax.legend(bbox_to_anchor=(0, 1.02, 1, .102), loc=3,ncol=2, borderaxespad=0,)

       



class SeaofBTCapp(tk.Tk):

    def __init__(self, *args, **kwargs):
        
        tk.Tk.__init__(self, *args, **kwargs)

        #tk.Tk.iconbitmap(self, default="clienticon.ico")
        #tk.Tk.wm_title(self, "Sea of BTC client")
        

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)




        self.frames = {}

        for F in (StartPage,PageOne):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(PageOne)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()

              
class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)


        label = tk.Label(self, text=("""do you want to start IRI meter."""), font=LARGE_FONT)


        label.pack(pady=10,padx=10)

        button1 = ttk.Button(self, text="Agree",
                            command=lambda: controller.show_frame(PageOne))
        button1.pack()



        


    
class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Page One!!!", font=LARGE_FONT)
        label.pack(pady=10,padx=10)



        canvas = FigureCanvasTkAgg(f, self)
       
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2Tk(canvas, self)
     
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        
        #canvas.move(50,50)
        



       
        toolbar.update()


        

        





app = SeaofBTCapp()
app.geometry("600x400")
ani = animation.FuncAnimation(f, animate, fargs=(xs, ys, ys1), interval=100)
app.mainloop()
Any help would be highly appreciated as the solution which I know is through threading but that's not working and I've read tkinter and threading are not the best companions. So what all could I do to make this program run smoothly.

Thank you!
Quote
#2
[Tkinter] How to deal with code that blocks the mainloop, freezing the gui
Quote
#3
Thanks Yoriz for the response it has helped me a lot to determine a correct step for my program!
Quote

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  “main thread is not in main loop” in Tkinter Long_r 1 1,501 Jun-26-2019, 11:00 PM
Last Post: metulburr
  Widget placement issues with tkinter grid thread 1 mgtheboss 2 1,590 Jan-09-2018, 03:59 PM
Last Post: SmokerX
  [Tkinter] Window unresponsive when executed. fawazcode 2 1,189 Sep-11-2017, 12:29 AM
Last Post: Larz60+

Forum Jump:


Users browsing this thread: 1 Guest(s)