1

I'm looking at the threading module in Python (version 3.4.3), and am having difficulty finding a way to update a variable in the target function that is called. I think I could create a global variable to share between the main program and the thread that I am starting, but found myself creating the following subclass instead. This seems to work for my purposes, but I'm curious if this is just a hack or if it's valid.

The goal here is to create a separate thread that regularly (and quietly) pings a server, and then updates a specified widget with the status of the update:

from tkinter import *
import threading

class ipthread(threading.Thread):
    def __init__(self, ip=None, labelobj=None):
        self.ip = ip
        threading.Thread.__init__(self, target=self.checkconnection, args=(labelobj,))

    def newip(self, ip):
        self.ip = ip

    def checkconnection(self, widget):
        while True:
            self.response = os.system("ping -c 1 -W 10 " + self.ip + " > /dev/null 2> /dev/null")
            if self.response==0:
                widget.config(text="connected", bg='green')
            else:
                widget.config(text="no connection", bg='red')
            time.sleep(1)

if __name__=="__main__":
    win = Tk()
    status = Label(win, text='')
    status.pack()
    ipchecker = ipthread(ip='192.168.1.1',widget=status)
    time.sleep(10)
    ipchecker.newip('192.168.1.2')

While I've put a simple routine here that calls the update after a 10-second delay, in my program the thread is initialized when I create a tkinter Frame control panel. The panel has a button that then calls the newip method to update the thread. It works, so I'm feeling like I've accomplished something, but is it overkill or unnecessary? I couldn't find another way to initialize the "checkconnection" routine as a separate thread, and then be able to update the ip address its using.

Topher
  • 498
  • 5
  • 17

1 Answers1

1

You ask several questions, I'll hopefully answer them all.

Is this a valid way to pass arguments to a thread?

There are two ways to pass arguments to a thread:

  • Subclass Thread, redefine the function run and access the new variables from there, as in

    class ipthread(threading.Thread):
      def __init__(self, ip=None, labelobj=None):
        self.ip = ip
        self.labelobj = labelobj
    
      def newip(self, ip):
        self.ip = ip
    
      def run(self):
        while True:
            self.response = os.system("ping -c 1 -W 10 " + self.ip + " > /dev/null 2> /dev/null")
            if self.response==0:
                self.labelobj.config(text="connected", bg='green')
            else:
                self.labelobj.config(text="no connection", bg='red')
            time.sleep(1)
    
  • Or you could specify the target to call

    def checkconnection(widget, ip):
        while True:
            self.response = os.system("ping -c 1 -W 10 " + self.ip + " > /dev/null 2> /dev/null")
            if self.response==0:
                widget.config(text="connected", bg='green')
            else:
                widget.config(text="no connection", bg='red')
            time.sleep(1)
    
    th = threading.Thread(target=checkconnection, args=(labelobj, ip))
    th.start()
    

Is this the proper solution for this problem?

No, it is not!

Widget libraries normally do not like a second thread to cut across their widgets. You could use a TKInter timer instead of a thread, or use a custom event to let the thread tell the widget that the text has changed.

When using a timer, the GUI becomes unresponsive while the tick is being handled. In your case, the ping has an unbounded delay so I would keep the thread and let it send a custom event.

Community
  • 1
  • 1
EvertW
  • 1,160
  • 9
  • 18
  • Thanks! That redefinition of "run" makes more sense; otherwise it seems I'm running a thread within a thread. However, the use of custom events as you mention does not seem to work. I can bind a custom event to the "status" object in my code above, and then call "event_generate" on it in the thread, but this still requires a bound function in the main program (or in a class, etc.) to run and update the interface object. How is this not different than having the thread do it directly? – Topher Jul 11 '15 at 15:49
  • The problem is not with where the code is defined but by which thread it is executed. GUI frameworks are not 'thread-safe'. They have their own thread that does all GUI work: the GUI's event loop. All direct interactions with the actual widgets should ONLY by executed by the event loop. Sending a custom event is the normal way of getting code executed by the event loop. – EvertW Jul 12 '15 at 14:14