user2591965
user2591965

Reputation: 11

Python thread runs even after closing WxPython application

the following code takes screenshots and logs keystrokes and mouse movements. It uses wxpython as GUI framework. I'm using python threads for screenshot and logging service. But whenever I close the GUI application. Threads are still running. How to stop those threads after closing the application?

import wx
import threading
import sys
import subprocess
import time

from pymouse import PyMouse
from pymouse import PyMouseEvent
from pykeyboard import PyKeyboard
from pykeyboard import PyKeyboardEvent

import pyscreenshot as ImageGrab
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw 
import gtk.gdk

import urllib2, urllib
import Cookie

MouseMovesCount = 0
MouseClicksCount = 0
KeyStrokesCount = 0

class OD_App(wx.App):
 def OnInit(self):
    frame = OD_MainFrame ("Login", (0, 0), (350, 200))
    self.SetTopWindow(frame)

    loginPanel = OD_LoginPanel (frame)
    self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
    frame.Show()
    return True

 def OnCloseWindow (self, event):
    self.Destroy()

class OD_MainFrame(wx.Frame):
 def __init__(self, title, pos, size):
    wx.Frame.__init__(self, None, -1, title, pos, size)
    self.CreateStatusBar()

class OD_LoginPanel(wx.Panel):
 def __init__(self, frame):  
    self.panel = wx.Panel(frame)
    self.frame = frame

    self.frame.SetStatusText("Authentication required!")
    self.showLoginBox()

 def showLoginBox (self):
  # Create the sizer
    sizer = wx.FlexGridSizer(rows=3, cols=2, hgap=5, vgap=15)

    # Username
    self.txt_Username = wx.TextCtrl(self.panel, 1, size=(150, -1))
    lbl_Username = wx.StaticText(self.panel, -1, "Username:")
    sizer.Add(lbl_Username,0, wx.LEFT|wx.TOP| wx.RIGHT, 50)
    sizer.Add(self.txt_Username,0, wx.TOP| wx.RIGHT, 50)

    # Password
    self.txt_Password = wx.TextCtrl(self.panel, 1, size=(150, -1), style=wx.TE_PASSWORD)
    lbl_Password = wx.StaticText(self.panel, -1, "Password:")
    sizer.Add(lbl_Password,0, wx.LEFT|wx.RIGHT, 50)
    sizer.Add(self.txt_Password,0, wx.RIGHT, 50)

    # Submit button
    btn_Process = wx.Button(self.panel, -1, "&Login")
    self.panel.Bind(wx.EVT_BUTTON, self.OnSubmit, btn_Process)
    sizer.Add(btn_Process,0, wx.LEFT, 50)

    self.panel.SetSizer(sizer)

 def OnSubmit(self, event):
    username = self.txt_Username.GetValue()
    password = self.txt_Password.GetValue()
    mydata = [('username',username),('password',password)]
    mydata = urllib.urlencode(mydata)
    path = 'http://xyz/logincheck.php'    #temporary db for testing
    req = urllib2.Request(path, mydata)
    req.add_header("Content-type", "application/x-www-form-urlencoded")
    page = urllib2.urlopen(req).read()
    if page == "true":
      self.frame.SetStatusText("Authentication Success!")
      self.show_other(username)
    else:
      self.frame.SetStatusText("Authentication Failed!")

 def OnCloseWindow (self, event):
    self.Destroy()

 def show_other(self,username):
    self.frame.Destroy()
    userpanel = OD_UserPanel()
    return True

class OD_UserPanel(wx.App):
 def OnInit(self):
    userpanel = wx.Frame(None,-1)
    self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
    #user_greeting = 'Welcome ' + username + '!'
    #username = wx.StaticText(userpanel, -1, user_greeting , style=wx.ALIGN_CENTRE)
    userpanel.Show()
    mouse_eventer = mouse_event()
    mouse_eventer.start()
    keyboard_eventer = key_event()
    keyboard_eventer.start()
    screenshot_eventer = screenshot_thread()
    screenshot_eventer.start()
    return True
 def OnCloseWindow (self, event):
    quit()
    event.Skip()
    raise SystemExit
class mouse_event(PyMouseEvent):
    def move(self, x, y):
        global MouseMovesCount
        MouseMovesCount = MouseMovesCount + 1
        print MouseMovesCount
    def click(self, x, y, button, press):
        global MouseClicksCount
            if press:
        MouseClicksCount = MouseClicksCount + 1
                print MouseClicksCount
            else:
                MouseClicksCount = MouseClicksCount + 1
                print MouseClicksCount

class key_event(PyKeyboardEvent):
    global screenshot_eventer
    def key_press(self, key):
        global KeyStrokesCount
        KeyStrokesCount = KeyStrokesCount + 1
        print KeyStrokesCount
    def key_release(self, key):
        global KeyStrokesCount
        KeyStrokesCount = KeyStrokesCount + 1
        print KeyStrokesCount

class screenshot_thread(threading.Thread):
  def __init__(self):
    super(screenshot_thread, self).__init__()
    self.state = True
  # Attributes
  def run(self):
    self.take_shot()
  def stop(self):
    self.state = False
    threading.Thread._Thread__stop()
  def take_shot(self):
     while self.state==True:
    time.sleep(10)
    subprocess.call(['scrot'])
if __name__ == '__main__':
    app = OD_App()
    app.MainLoop()

Upvotes: 1

Views: 1382

Answers (2)

mprochnow
mprochnow

Reputation: 11

You defined a stop method in your screenshot_thread class but you does not use it. Calling it in the method OnCloseWindow should do the job.

Upvotes: 0

mata
mata

Reputation: 69032

Don't call threading.Thread._Thread__stop! The leading underscore is a sign that this is internal api, it's not guaranteed to even exist (in fact, in python3 it's gone).

If you wish the thead to be destroyed automatically, set it to be daemonic:

def __init__(self):
    super(screenshot_thread, self).__init__()
    self.daemon = True

That will cause it to be automatically destroyed when the last non-daemonic thread has stopped. Or, in your case, just setting the state to False should make the thread exit after 10 seconds.

Upvotes: 2

Related Questions