title: Using multithreads on a pygtk application to avoid GUI freeze
body: <p>Currently developing a relativaly complex GUI using Glade for GTK+ 3 and not having problems understanding the "how to-s" for connecting buttons and signals and i have pretty good knowledge on threads.</p>
  
  <p>This is a code of a simplified version of my app, where the threads are working:</p>
  
  <pre><code>#!/usr/bin/env python
  import threading,logging
  from gi.repository import Gtk,GObject,Gdk 
  import os
  import time
  logging.basicConfig(level=logging.DEBUG,
                  format='[%(levelname)s] (%(threadName)-10s) %(message)s',
                  ) 
  Path=os.path.dirname(os.path.realpath(__file__))
  GObject.threads_init()
  
  class MyThread(threading.Thread,GObject.GObject):
  __gsignals__ = {
     "completed": (
              GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, [])
  }
  
  def __init__(self,*args):
      threading.Thread.__init__(self)
      GObject.GObject.__init__(self)
      self.cancelled = False
      self.name = args[0]
      self.setName("%s" % self.name)
  def run(self):
      print "Running %s" % str(self)
      #actual work thread is doing----------
      time.sleep(3)
      #--------------------------------------
      logging.debug("Emiting signal...")
      GObject.idle_add(self.emit,"completed")
      logging.debug("Thread ending...")
      logging.debug("Done.")
  
  class GUI(object):
  def __init__(self):
      #build the GUI
      window = Gtk.Window()
      window.set_default_size(200, 200)
      vbox = Gtk.VBox(False, 5)
      hbox = Gtk.HBox(True, 5)
      self.spinner = Gtk.Spinner()
      self.button = Gtk.Button('Start')
      window.connect('destroy', Gtk.main_quit)
      self.button.connect('clicked', self.startAnimation)
      window.add(vbox)
      vbox.pack_start(Gtk.Label("Something"), False, False, 0)
      vbox.pack_start(self.spinner, True, True, 0)
      vbox.pack_end(hbox, False, False, 0)
      hbox.pack_start(self.button,True,True,0)
      window.show_all()
      Gtk.main()
  
  
  def startAnimation(self,*args):
      self.button.set_sensitive(False)
      self.spinner.start()
      thread=MyThread("Tsat_thread")
      thread.connect("completed",self.completed_thread)
      thread.start()
  
  
  def completed_thread(self,*args):
      #the subprocess call ended successfully so we can continue without problems
      #updating the result of the capture
      logging.debug("Function called at ending thread...")
      print "COMPLETED signal catched"
      self.spinner.stop()
      self.spinner.props.visible=False
      logging.debug("Done.")
  
  print "Start of main GUI"
  gui = GUI()
  #print "mostro la finestra"  
  </code></pre>
  
  <p>A thread is created for managing an external process which otherwise wold freeze the interface. When the thread finishes a "completed" signal is emitted and caught from the MainThread (Which as far as i know is the only one who can access the window and apply changes)
  This is the output of the program:</p>
  
  <pre><code>Start of main GUI 
  Running MyThread(Tsat_thread, started 139636004558592)   
  [DEBUG] (Tsat_thread) Emiting signal...   
  [DEBUG] (Tsat_thread) Thread ending...   
  [DEBUG] (Tsat_thread) Done.   
  [DEBUG] (MainThread) Function called at ending thread...   
  COMPLETED signal catched  
  [DEBUG] (MainThread) Done.
  </code></pre>
  
  <p>When I try the same method on my application the "completed" signal is not caught from the MainThread. I want to understand where is the problem. The relevant part of the code not working:</p>
  
  <pre><code>import subprocess,threading,logging
  from gi.repository import Gtk,GObject,Gdk 
  import os,datetime
  import timeit
  logging.basicConfig(level=logging.DEBUG,
                  format='[%(levelname)s] (%(threadName)-10s) %(message)s',
                  ) 
  
  Path=os.path.dirname(os.path.realpath(__file__))
  GObject.threads_init()
  ...
  class MyThread(threading.Thread,GObject.GObject):
  __gsignals__ = {
     "completed": (
              GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, [])
  }
  
  def __init__(self,*args):
      threading.Thread.__init__(self)
      GObject.GObject.__init__(self)
      self.cancelled = False
      self.name = args[0]
      self.interface=args[1]
      self.duration=args[2]
      self.conf_tstat=args[3]
      self.setName("%s" % self.name)
  def run(self):
      print "Running %s" % str(self)
      #------------------------------------------------------------
      local_conf_file=self.conf_tstat.replace(Path,".")
      cmd = "dumpcap -a duration:"+self.duration+" -i "+self.interface+" -P -w - |tstat s capture -N "+local_conf_file+" stdin"
      time=datetime.datetime.now().strftime('%H_%M_%d_%b_%Y')
      with open("temp_log","wb") as logfile:
          logfile.write(time+".out\n")
          process = subprocess.Popen(cmd, shell=True, universal_newlines=True,  stdout=logfile)
          process.wait()
      #------------------------------------------------------------------
      logging.debug("Emiting signal...")
      GObject.idle_add(self.emit,"completed")
      logging.debug("Thread ending...")
      logging.debug("Done.")
  ...
  # On button execute clicking 
  def on_button_execute_clicked(self, *args):
  
      if (self.builder.get_object("radio_online").get_active()):
          print "#Online"
          self.builder.get_object("spinner1").start()
          self.builder.get_object("spinner1").props.visible=True
          entries=self.get_entries()
  
          thread=MyThread("Tsat_thread",entries[0],entries[1],entries[2])
          thread.connect("completed",self.completed_tstat)
          thread.start()
  
      else:
          print "#Offline"
          ...
  ...
  #function called after Tstat_thread finishes
  def completed_tstat(self,*args):
      #the subprocess call ended successfully so we can continue without problems
      #updating the result of the capture
      logging.debug("Function called at ending thread...")
      print "COMPLETED signal catched"
      with open("temp_log","rb") as logfile:
          capture_no=logfile.readline()
      capture_no=capture_no.strip('\n')   
      self.builder.get_object("capture_entry").set_text(Path+"/capture/"+capture_no)
      self.list_of_entries = ["capture_entry","output_entry"]
      entries = self.get_entries()
      if entries[1]=='':
          #default output folder
          self.builder.get_object("output_entry").set_text(Path+"/output")
          entries= self.get_entries()
  
      #deleting possible results from a previous capture
      rm="rm -f "+entries[1]+"/log_* "+entries[1]+"/outLog.*"
      subprocess.call(rm.split(),shell=False)
  
      #triminng the info from the tstat log files
      command="python ./python/trimInfo.py -t "+entries[0]+ "/log_tcp_complete, "+entries[0]+ "/log_tcp_nocomplete -u "+entries[0]+"/log_udp_complete -o "+entries[1]
      process=subprocess.Popen(command.split(),shell=False,stdout=subprocess.PIPE)
      process.wait()
      returncode=process.poll()
      if(returncode==0):
          self.builder.get_object("entry_output_folder").set_text(entries[1])
          self.builder.get_object("button_show_out_folder").props.sensitive=True
          self.show_info("Capture+Transformation of traffic data ended successfully!")
          self.set_page_complete()
      else:
          self.my_log+=returncode
          self.handle_program_error(my_log)
  
      self.builder.get_object("spinner1").stop()
      self.builder.get_object("spinner1").props.visible=False
      logging.debug("Done.")
  </code></pre>
  
  <p>The completed_tstat is the callback but is never called on my app. The output from my app when i try the multithreading is below:`</p>
  
  <pre><code>Start of main GUI
  #Online
  device_entry= 'wlan0'
  time_spinbutton= '10'
  tstat_conf_entry= './codicePippo/tstat-2.3/tstat-conf/net.private'
  capture_entry= './capture/'
  output_entry= './output'
  Running &lt;MyThread(Tsat_thread, started 140689548355328)&gt;
  Capturing on wlan0
  File: -
  
  Packets: 6 
  Packets: 7 
  Packets: 10 
  Packets: 13 
  Packets: 14 
  Packets: 15 
  Packets: 20 
  Packets: 23 
  Packets: 24 
  Packets captured: 24
  Packets received/dropped on interface wlan0: 24/0 (100.0%)
  
  WARN: This udp flow is neither incoming nor outgoing: src - 192.168.1.27; dst - 192.168.1.255!
  [DEBUG] (Tsat_thread) Emiting signal...
  [DEBUG] (Tsat_thread) Thread ending...
  [DEBUG] (Tsat_thread) Done.
  </code></pre>