User avatar
joseplaselva
Posts: 71
Joined: Tue Oct 04, 2016 4:45 am

TKINTER GUI and Thread

Mon Oct 22, 2018 8:15 am

Hi, I have a GUI made with Tkinter thatr have a label widget where is displaying in streaming a webcam , and some buttons that make some actions, the problem is that when I press a button and executes an action the video freezes , I know that exist the thread method to do paralel actions but after googling a lot I can't found the way to aplicate in my case, Can someone with more knowledge help me to correct my script to handle both actions, video looping and button action. Hereby I paste a resumed exemple of my script, because the spped test takes some time to realize the video gets freeze till the end of the test.

Code: Select all

from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import subprocess
import urllib

class Application:
    def __init__(self, output_path="./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0)  # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera

        self.root = tk.Tk()  # initialize root window
        w = 655  # 655width for the Tk root
        h = 665  # 585height for the Tk root


        ws = self.root.winfo_screenwidth()  # width of the screen
        hs = self.root.winfo_screenheight()  # height of the screen

        # calculate x and y coordinates for the Tk self.root  window
        x = (ws / 2) - (w / 2)
        y = (hs / 2) - (h / 2)

        # set the dimensions of the screen
        # and where it is placed
        self.root.geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("   SAFETY SWITCH DEVICES     ")  # set window title
        # self.destructor function gets fired when the window is closed
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=10, column=0, columnspan=20, padx=4, pady=6)

        self.botNET = tk.Button(self.root, width=18, font=('arial', 18, 'normal'), bg="grey", text=led_text,
                                activebackground="#ffff00")
        self.botNET.grid(row=15, rowspan=1, column=0, columnspan=3, pady=2, padx=2)
        self.botNET.configure(command=self.ledlight)

        self.video_loop()

    def video_loop(self):
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image
        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds


    def servoNET(self):
        stri = "https://www.google.co.in"
        data = urllib.urlopen(stri)
        site = urllib.urlopen("http://checkip.dyndns.org/").read()
        grab = str(re.findall('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', site))

        caracters = len(grab)
        string = grab[1:(caracters - 1)]
        myIP = ""
        for i in string:
            # If one of the characters is a number, add it to the empty string
            if i in '1234567890.':
                myIP += i

        response = subprocess.Popen('speedtest-cli --simple', shell=True, stdout=subprocess.PIPE).stdout.read()

        download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
        upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)

        download[0] = download[0].replace(',', '.')
        upload[0] = upload[0].replace(',', '.')

        result = "IP: " + myIP + "  | Down:" + str(download[0]) + " Mbit/s  |  Up:" + str(upload[0]) + " Mbit/s"

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./",
                help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()









User avatar
paddyg
Posts: 2227
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 2:10 pm

worked ok for me but I had to add:

Code: Select all

        self.panel.grid(row=0, rowspan=10, column=0, columnspan=20, padx=4, pady=6)
        led_text = "led text" #<<<<<<<<<<<<<<<<<<<<<<<<<
        self.botNET = tk.....

    def destructor(self):    #<<<<<<<<<<<<<<<<<<<<<<<<<
        print('destroyed')   #<<<<<<<<<<<<<<<<<<<<<<<<<
    def ledlight(self):      #<<<<<<<<<<<<<<<<<<<<<<<<<
        print('ledlight lit')#<<<<<<<<<<<<<<<<<<<<<<<<<
...
and (obviously!!) change Tkinter to tkinter
PS tkinter is already providing a threaded environment so you don't generally need to add to it.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
joseplaselva
Posts: 71
Joined: Tue Oct 04, 2016 4:45 am

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 4:52 pm

Hi, thanks for your fast reply, but sorry I don't understand your changes.... Because I was cutting my script to show here ,I make some mistakes. the comand of the self.botNET is " self.servoNET ( not self.ledlight ), and I forgot the " def destructor(self) and "import re ". I have tested and it takes about 38 seconds to ping anb obtaint download and upload speeds, an the streaming is freezes.
I'm using Tkinter because I'm working in my raspberry py with Python 2,7 .
Can you please explain where put the changes you mean, many thanks in advance for your kindness.
here is the code with the mofifications and tested ok , video freezes about 38-40 seconds :

Code: Select all

from PIL import Image, ImageTk
import Tkinter as tk
import argparse
import time
import datetime
import cv2
import os
import re
import subprocess
import urllib

class Application:
    def __init__(self, output_path="./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
        self.vs = cv2.VideoCapture(0)  # capture video frames, 0 is your default video camera
        self.output_path = output_path  # store output path
        self.current_image = None  # current image from the camera

        self.root = tk.Tk()  # initialize root window
        w = 655  # 655width for the Tk root
        h = 665  # 585height for the Tk root


        ws = self.root.winfo_screenwidth()  # width of the screen
        hs = self.root.winfo_screenheight()  # height of the screen

        # calculate x and y coordinates for the Tk self.root  window
        x = (ws / 2) - (w / 2)
        y = (hs / 2) - (h / 2)

        # set the dimensions of the screen
        # and where it is placed
        self.root.geometry('%dx%d+%d+%d' % (w, h, x, y))
        self.root.title("   SAFETY SWITCH DEVICES     ")  # set window title
        # self.destructor function gets fired when the window is closed
        self.root.protocol('WM_DELETE_WINDOW', self.destructor)

        self.panel = tk.Label(self.root)  # initialize image panel
        self.panel.grid(row=0, rowspan=10, column=0, columnspan=20, padx=4, pady=6)

        self.botNET = tk.Button(self.root, width=18, font=('arial', 18, 'normal'), bg="grey", text="check network",
                                activebackground="#ffff00")
        self.botNET.grid(row=15, rowspan=1, column=0, columnspan=3, pady=2, padx=2)
        self.botNET.configure(command=self.servoNET)

        self.video_loop()

    def video_loop(self):
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
        if ok:  # frame captured without any errors
            cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)  # convert colors from BGR to RGBA
            self.current_image = Image.fromarray(cv2image)  # convert image for PIL
            imgtk = ImageTk.PhotoImage(image=self.current_image)  # convert image for tkinter
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image
        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds


    def servoNET(self):
        stri = "https://www.google.co.in"
        data = urllib.urlopen(stri)
        site = urllib.urlopen("http://checkip.dyndns.org/").read()
        grab = str(re.findall('[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', site))

        caracters = len(grab)
        string = grab[1:(caracters - 1)]
        myIP = ""
        for i in string:
            # If one of the characters is a number, add it to the empty string
            if i in '1234567890.':
                myIP += i

        response = subprocess.Popen('speedtest-cli --simple', shell=True, stdout=subprocess.PIPE).stdout.read()

        download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
        upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)

        download[0] = download[0].replace(',', '.')
        upload[0] = upload[0].replace(',', '.')

        result = "IP: " + myIP + "  | Down:" + str(download[0]) + " Mbit/s  |  Up:" + str(upload[0]) + " Mbit/s"

    def destructor(self):
        self.root.destroy()
        self.vs.release()  # release web camera
        cv2.destroyAllWindows()  # it is not mandatory in this application


# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", default="./",
                help="path to output directory to store snapshots (default: current folder")
args = vars(ap.parse_args())

# start the app
pba = Application(args["output"])
pba.root.mainloop()









User avatar
paddyg
Posts: 2227
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 5:25 pm

Ah, that makes more sense. I'm not sure exactly why it stalls but you might be able to speed it up as you suggested with a thread. It's not clear what the `result` is for - you don't use it in any way. Assuming you want to get the string back into the instance of Application you could do this kind of thing:

Code: Select all

import re
import subprocess
import urllib.request
import threading

def do_servoNet(app):
    stri = "https://www.google.co.in"
    data = urllib.request.urlopen(stri)
    site = urllib.request.urlopen("http://checkip.dyndns.org/").read()
    grab = str(re.findall(b'[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+', site))

    caracters = len(grab)
    string = grab[1:(caracters - 1)]
    myIP = ""
    for i in string:
        # If one of the characters is a number, add it to the empty string
        if i in '1234567890.':
            myIP += i

    response = subprocess.Popen('speedtest-cli --simple', shell=True, stdout=subprocess.PIPE).stdout.read()

    download = re.findall('Download:\s(.*?)\s', response, re.MULTILINE)
    upload = re.findall('Upload:\s(.*?)\s', response, re.MULTILINE)

    download[0] = download[0].replace(',', '.')
    upload[0] = upload[0].replace(',', '.')

    app.result = "IP: " + myIP + "  | Down:" + str(download[0]) + " Mbit/s  |  Up:" + str(upload[0]) + " Mbit/s"
...
class Application:
    def __init__(self, output_path="./"):
        """ Initialize application which uses OpenCV + Tkinter. It displays
            a video stream in a Tkinter window and stores current snapshot on disk """
...
            self.panel.imgtk = imgtk  # anchor imgtk so it does not be deleted by garbage-collector
            self.panel.config(image=imgtk)  # show the image
        self.root.after(30, self.video_loop)  # call the same function after 30 milliseconds

    def servoNET(self):
        t = threading.Thread(target=do_servoNet, args=(self,))
        t.start()

    def destructor(self):
        self.root.destroy()
        self.vs.release()  # release web camera
...
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
joseplaselva
Posts: 71
Joined: Tue Oct 04, 2016 4:45 am

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 6:08 pm

Great, many thanks , now it's running video withouth freezing, so if I understand, because my GUI have several buttons that make differents actions, I must put all of them out of the Main loop before the Class Application ? , one more thing, how to make to show the results of the actions in a label , the label is defined as "self.txtNet" but if I put

Code: Select all

        self.textNET.delete("1.0", tk.END)
        self.textNET.insert(tk.END,app.result)
inside " def do_servoNet(app):" it doesn't reconize the name "self" : "NameError: global name 'self' is not defined#
Thanks again for your help

User avatar
paddyg
Posts: 2227
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 6:22 pm

Glad it improved things. By passing the Application instance as an argument to the threaded function you can write `result` as a property, that means methods of the class can then get that as self.result It would be reasonably logical to check if self.result had changed in your existing 30ms loop. Something like:

Code: Select all

        self.result = None # << so it doesn't raise an error in video_loop
        self.video_loop()

    def video_loop(self):
        if self.result is not None:
            self.textNET.delete("1.0", tk.END)
            self.textNET.insert(tk.END, self.result)
            self.result = None # set it to None after changing display
        """ Get frame from the video stream and show it in Tkinter """
        ok, frame = self.vs.read()  # read frame from video stream
It's probably tidier to put each button into its own thread - not all of them might need to be executed that way though.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

User avatar
joseplaselva
Posts: 71
Joined: Tue Oct 04, 2016 4:45 am

Re: TKINTER GUI and Thread

Mon Oct 22, 2018 6:50 pm

Thanks again, it runs perfectly, you really save me a lot of time. Thanks.
My GUI has several buttons that comand servos , that's why I needed a continuos streaming to check the movements of the servos because
I use in a remote place. And I want to see if they work how they must to do.
Have a nice evening
Cheers

Return to “Python”