0

I am trying to create a GUI python interface to display the value from sensors which are connected to an arduino via serial communication. For this interface I am using a class named application().

I first created the GUI application using Tkinter which is functional. That is to say that the different elements (frame, widgets, etc.) are displayed correctly.

Then I created a function (get_data), to retrieve the data from the arduino. Each byte I retrieve the value and store it in an array with the associated key (the name of the sensors). Then I retrieve the array for the parse and assign the values ​​in variables (integer or float) so that I can retrieve them in the GUI via the function (update_data).

To avoid having to worry between displaying and looping data, I decided to use the library threading to run the get_data() and update_data() functions continuously in other threads.

when I launch the application, the code loops on get_data() (see the print below), but the Tkinter interface does not launch.

Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data
Wait, sending data

Here are the two functions to retrieve data:

def get_data(self):
    ser = serial.Serial('/dev/cu.usbmodem14201', 9600)
    ser.flushInput()
    index=0
    self.currentDataArray = np.array(np.zeros([index]))
    while True:
        try:
            for c in ser.readline():
                current_decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
                print(current_decoded_bytes)
                self.currentDataArray = np.append(self.currentDataArray,current_decoded_bytes)
                if c == '\n':
                    return self.currentDataArray
                    self.currentDataArray = np.array(np.zeros([index]))
        except:
            print("Wait, sending data")
        pass

def Update_value(self, currentDataArray,update_period):
    print("updating data")
    new = time.time()
    print(self.currentDataArray)
    analogValue=float()
    VoltageEcSensor=float()
    tempWaterSensor=float()
    TemperatureB=float()
    HumidityB=float()
    pHvalue=float()
    EcSensorValue=float()
    extractorStatement=int()
    ligthStatement=int()
    intractorStatement=int()
    fanStatement=int()

    while(1):
            currentDataArray.all()
            try:
                self.analogValue=self.currentDataArray[0]
                self.VoltageEcSensor=self.currentDataArray[1]
                self.tempWaterSensor=self.currentDataArray[2]
                self.TemperatureB=self.currentDataArray[3]
                self.HumidityB=self.currentDataArray[4]
                self.pHvalue=self.currentDataArray[5]
                self.EcSensorValue=self.currentDataArray[6]
                self.extractorStatement=self.currentDataArray[7]
                self.ligthStatement=self.currentDataArray[8]
                self.intractorStatement=self.currentDataArray[9]
                self.fanStatement=self.currentDataArray[10]
            except:
                pass

            if time.time() - new >= update_period:
                self.analogValue=0
                self.VoltageEcSensor=0
                self.tempWaterSensor=0
                self.TemperatureB=0
                self.HumidityB=0
                self.pHvalue=0
                self.EcSensorValue=0
                self.extractorStatement=0
                self.ligthStatement=0
                self.intractorStatement=0
                self.fanStatement=0
                new = time.time()
            pass

Here is a function to acquire data continuously using other threads:

def threading(self):
        multi = threading.Thread(target = self.get_data())
    #   multi.deamon = True
        multi.start()
        multi2 = threading.Thread(target = self.Update_value(self.currentDataArray, self.update_period))
    #   multi2.deamon = True
        multi2.start()

This is how I initialize in the tkinter interface:

class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        update_period = 5
        tempBox=self.TemperatureB
        tempExtBox=23
        humidityBox=self.HumidityB
        TempSolution=self.tempWaterSensor
        pH_solution=self.pHvalue
        Ec_solution=self.EcSensorValue
        Global_conso=110
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        # set all width and height for each Pane
        width_MODEpane=screen_width/3
        height_MODEpane=screen_height/4
        self.MainWindow(screen_height, screen_height)

if __name__ == "__main__":
    app = Application()
    app.threading()
    app.title("Automate GUI app")
    app.mainloop()

I think I'm not using correctly the threading library, do you have any suggestion to solve this issue?

martineau
  • 119,623
  • 25
  • 170
  • 301
Elcastor
  • 9
  • 2
  • 1
    You really shouldn't use `threading` when using `tkinter`. Sometimes `tkinter` can crash without even giving you an error message. But try changing `threading.Thread(target=self.get_data())` to `threading.Thread(target=self.get_data)` – TheLizzard Apr 19 '21 at 21:38
  • You can also try using [`.after()`](https://www.geeksforgeeks.org/python-after-method-in-tkinter/) – Matiiss Apr 19 '21 at 21:41
  • Generally speaking, threads prevent the `mainloop()` of the tkinter GUI from running which will make it appear to freeze or hang. You can mitigate that by using my answer to [Freezing/Hanging tkinter Gui in waiting for the thread to complete](https://stackoverflow.com/questions/53696888/freezing-hanging-tkinter-gui-in-waiting-for-the-thread-to-complete). – martineau Apr 19 '21 at 22:02
  • 1
    `threading.Thread(target = self.get_data())` does _not_ run `self.get_data` in a separate thread. – Bryan Oakley Apr 19 '21 at 22:05
  • @martineau Most of the time that isn't true. In the example you have the question used `.join()` which is a blocking call. That is what blocked the GUI from updating. In this case OP doesn't use `.join()` so the GUI isn't going to freeze. – TheLizzard Apr 19 '21 at 22:44
  • As @TheLizzard and @Bryan Oakley wrote, when specifying an object in the `target` argument of threading.thread, do not add `()` to the method name etc. With `()`, it specifies the execution result of the object, not the callable object. Then you cannot start the thread. Also, if the callable object requires a parameter, specify it in another `args` parameter. [class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)](https://docs.python.org/3/library/threading.html#threading.Thread) – kunif Apr 19 '21 at 22:58
  • @TheLizzard: I disagree. It's often true assuming the threading it done correctly (which is why the authors had the recipe for it in their Python cookbook). The fundamental problem (again assuming it's done properly) is that **`tkinter` doesn't support multithreading**. Even when it's done properly with only one thread making GUI calls, it can still interfere with the `mainloop` if the non-GUI thread(s) don't relinquish control often or quickly enough. – martineau Apr 20 '21 at 00:43
  • @kunif: While what you suggest is the correct way to create a thread object, it's not guaranteed to fix the OP's problem because of the fact that `tkinter` doesn't support multithreading. – martineau Apr 20 '21 at 00:49
  • Elcastor: Some advice — never use a bare `except:` because it can hide unexpected exceptions. If you don't know which ones might occur, use `except Exception as exc:` and then put a `print(exc)` on the line below it so you'll get a clue as to what's happening. – martineau Apr 20 '21 at 00:58
  • That's right, @martineau. So I made a comment instead of an answer. However, even if tkinter itself does not support multithreading, it will be possible for tkinter and multithreading to coexist. These are articles in Japanese, but there are things like these. [\[Tkinter\] GUIの応答性をよくする](https://qiita.com/kotai2003/items/dc0b23fa95844fc1ba45), [\[Tkinter\] Eventオブジェクトでスレッドを制御する](https://qiita.com/kotai2003/items/db7c846e0d4e2d6d6d45) – kunif Apr 20 '21 at 01:27
  • @kunif: I'm well aware that they can coexist. If nothing else, you can tell by my answer to the linked question. I have also posted other answer using them at various times on this site, and I'm sure there are a number by other people. The only reason I made my earlier comment to you was to make sure the OP did get the idea that fixing the creation of the threading object would solve their problem — sorry if you took it the wrong way. – martineau Apr 20 '21 at 01:34
  • @Matiiss How it works ? I will check the documentation. Do you think Is better than threading methods? – Elcastor Apr 21 '21 at 22:27
  • @Kunif TheLizzard martineau I appreciate that you discuss about my issue. Thanks for that. I still a little be lost, should I use other methods to get the data from the arduino ? Because If I don't use threading methods, the GUI interface run but the panel showing the data show only once the value and freeze. I remain open to any other method than threading...Should I create a second class for getting the data with threading ? Thanks in advance – Elcastor Apr 21 '21 at 22:27
  • Elcastor: If you're following the pattern in my answer to the linked question, then in the GUI part of your program should be periodically checking a queue in the `processIncoming()` method for messages from the arduino that your `get_data()` function put into it. The `ThreadedClient` part should be putting data from arduino into that queue. When a message is received by `processIncoming()` then the `Update_value()` function could be called with information from the message. This salient point is that the `ThreadedClient` part doesn't make _any_ calls having to do with the tkinter GUI. – martineau Apr 21 '21 at 22:49
  • If you just need to repeat at time intervals, you can use .after() as suggested by @Matiiss. This article may be helpful. [Tkinter update variable real time](https://stackoverflow.com/q/46279096/9014308) – kunif Apr 21 '21 at 23:00

0 Answers0