Jakub Szlaur
Jakub Szlaur

Reputation: 2132

Separating Tkinters GUI and control of the application

1) What is my goal: I’m creating an application that should read data every 60s from ModBusServer, append those data to Graphs and then when the app is closed save the data to excel file.

Site note: The process of reading data from ModBusServer and appending them to graphs should start after a start button is pressed. And end after stop button is pressed OR when ModBusServer sends a request to stop.

2) What I have so far: I created the GUI without any major problems as a class “GUI_komora”. Everything there works just fine.

3) What is the problem: But now I’m lost on how to approach the “read data every 60 seconds”, and overall how to control the application.

I did some research on threading but still I’m confused how to implement this to my application. I learned how to make functions run simultaneously in this tutorial. And also how to call a function every few seconds using this question.

But none of them helped me to learn how to control the overall flow of the application.

If you could redirect me somewhere or tell me about a better approach I would be really glad.

Some of my code:

from tkinter import *

from GUI_komora import GUI

root = Tk()
my_gui = GUI(root) #my GUI class instance

#main loop
root.mainloop()

"""

How do I achieve something like this???

whenToEnd = False
while whenToEnd:
    if step == "Inicialzation":
        #inicializace the app

    if step == "ReadData":
        #read data every 60 seconds and append them to graphs

    if step == "EndApp"
        #save data to excel file and exit app
        whenToEnd = True 


"""

Upvotes: 0

Views: 53

Answers (1)

Ananya
Ananya

Reputation: 101

Here is an example of a loop that takes a decision (every 60 sec in your case) and pushes the outcome of the decision to tkinter GUI: https://github.com/shorisrip/PixelescoPy/blob/master/base.py Parts:

  1. main thread - starts tkinter window
  2. control thread - reads some data and decides what to show in GUI
  3. GUI class - has a method "add_image" which takes input an image and displays on GUI.(add_data_to_graph maybe in your case). This method is called everytime by the control thread.

Snippets:

def worker(guiObj, thread_dict):
    # read some data and make decision here
    time.sleep(60)
    data_to_show = <outcome of the decision>
    while some_logic:
        pictureObj = Picture(chosen, timer)
        pictureObj.set_control_thread_obj(thread_dict["control_thread"])
        guiObj.set_picture_obj(pictureObj)
        pictureObj.display(guiObj)
        # do post display tasks
    guiObj.quit_window()

# Keep GUI on main thread and everything else on thread
guiObj = GuiWindow()
thread_list = []
thread_dict = {}
thread_for_image_control = threading.Thread(target=worker, args=(guiObj,
                                                                 thread_dict))
thread_dict["control_thread"] = thread_for_image_control
thread_list.append(thread_for_image_control)
thread_for_image_control.start()
guiObj.run_window_on_loop()
# thread_for_image_control.join()

Code for Picture class:

class Picture:
    def __init__(self, path, timer):
        self.path = path
        self.timer = timer
        self.control_thread_obj = None

    def set_control_thread_obj(self, thread_obj):
        self.control_thread_obj = thread_obj

    def display(self, guiObj):
        image_path = self.path
        guiObj.add_image(image_path)
        time.sleep(self.timer)

Code for GUI class

class GuiWindow():
    def __init__(self):
        self.picture_obj = None
        self.root = Tk()
        self.image_label = None
        self.image = None
        self.folder_path = None
        self.timer = None
        self.root.protocol("WM_DELETE_WINDOW", self.exit_button)

    def add_image(self, image_path):
        resized_img = self.resize(image_path)
        image_obj = ImageTk.PhotoImage(resized_img)
        image_label = Label(self.root, image=image_obj,
                            height=resized_img.height,
                            width=resized_img.width)
        self.image = image_obj # DO NOT REMOVE - Garbage collector error
        if self.image_label is not None:
            self.remove_image()
        image_label.grid(row=0, column=0, columnspan=3)
        self.image_label = image_label

Here based on my control loop thread I am changing image (in your case graph data) of the tkinter GUI. Does this help?

Upvotes: 1

Related Questions