Help: win32gui & threads
Miki Tebeka
tebeka at cs.bgu.ac.il
Mon Jun 17 07:18:05 EDT 2002
Hello All,
I've written a little logger daemon with UI in Windows taskbar.
(See code below)
It seems to work fine but when I choose 'Quit' the windown closes but
the pythonw is still alive and I need to kill it from the TaskManager.
Any idea how to make it close?
Thanks.
Miki.
-----------------------------------------
#!/usr/bin/env python
'''Logger daemon
Opens socket server and write messages to log file
'''
__author__ = 'Miki Tebeka <miki_tebeka at amat.com>'
__version__ = '$Revision: 1.3 $'
# $Source: /home/miki/.cvsroot/logging/logd.py,v $
import SocketServer
import socket
import Queue
import threading
import time
import re
from win32api import *
from win32gui import *
import win32con
import sys
import os, os.path
MESSAGE_QUEUE = Queue.Queue(0) # Thread safe message queue with no
limit
# These two are not protected by mutex since only the main thread
write there and
# the worker thread reads them
WRITE = 1 # Write flag
NEW_LOG = 0 # Create new file
def error(msg):
'''Error function'''
MessageBox(win32con.MB_OK, msg)
class SocketListener(SocketServer.BaseRequestHandler):
'''Socket listener. Reads line from socket and place it in message
queue'''
def __init__(self, request, client_address, server):
'''Constructor'''
SocketServer.BaseRequestHandler.__init__(self, request,
client_address, server)
def handle(self):
'''Handler function. Reads line from socket and writes to
queue'''
msg = ''
while 1:
try:
c = self.request.recv(1)
if not c:
return
if (c == '\n'):
MESSAGE_QUEUE.put(msg)
msg = ''
else:
msg += c
except socket.error, e:
error('Error: %s' % e)
return
class LoggerDaemon(SocketServer.ThreadingTCPServer, threading.Thread):
'''Logger Daemon. Listens on given port and threads listeners upon
requests'''
def __init__(self, port):
self.port = port
self.addr = ('', self.port)
threading.Thread.__init__(self)
SocketServer.ThreadingTCPServer.__init__(self, self.addr,
SocketListener)
def run(self):
self.serve_forever()
class LogWriter(threading.Thread):
'''Log writer. Get messages from queue and write to file'''
def __init__(self, log_dir):
self.log_dir = log_dir
if not os.path.isdir(self.log_dir):
try:
os.makedirs(self.log_dir)
except OSError, ose:
error("Can't create log directory %s (error was: %s)"
% (self.log_dir, ose))
sys.exit(1)
self.new_log()
threading.Thread.__init__(self)
def run(self):
'''Thread function'''
global NEW_LOG, WRITE
while 1:
msg = MESSAGE_QUEUE.get()
if NEW_LOG:
self.new_log()
NEW_LOG = 0 # This is a sync NO-NO but we can live
with that
if WRITE:
print >> self.fp, msg.strip()
self.fp.flush()
def new_log(self):
'''New log file'''
logname = '%s/%s' % (self.log_dir, re.sub('(\s+)|:', '_',
time.ctime()))
self.log_file = logname
self.fp = open(self.log_file, 'a')
class MainWindow:
'''Main window for handling taskbar notifications'''
def __init__(self):
'''Constructor'''
self.ID_TBAR = win32con.WM_USER + 20
self.icon_file = './logd.ico'
self.title = 'BeeLogger Daemon'
self.IDM_TOGGLE, self.IDM_NEWLOG, self.IDM_QUIT = 1023, 1024,
1025
self.resume_pause_msg = ('Resume', 'Pause')
message_map = {
win32con.WM_DESTROY: self.OnDestroy,
win32con.WM_COMMAND: self.OnCommand,
self.ID_TBAR : self.OnTaskbarNotify,
}
# Register the Window class.
wc = WNDCLASS()
hinst = wc.hInstance = GetModuleHandle(None)
wc.lpszClassName = "BeeLoggerDaemon"
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW;
wc.hCursor = LoadCursor( 0, win32con.IDC_ARROW )
wc.hbrBackground = win32con.COLOR_WINDOW
wc.lpfnWndProc = message_map # could also specify a wndproc.
classAtom = RegisterClass(wc)
# Create the Window.
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
self.hwnd = CreateWindow( classAtom, self.title, style, \
0, 0, win32con.CW_USEDEFAULT,
win32con.CW_USEDEFAULT, \
0, 0, hinst, None)
UpdateWindow(self.hwnd)
# Try and find a custom icon
if not os.path.isfile(self.icon_file):
icon_flags = win32con.LR_LOADFROMFILE |
win32con.LR_DEFAULTSIZE
self.hicon = LoadIcon(0, win32con.IDI_APPLICATION)
else:
self.hicon = LoadImage(hinst, self.icon_file,
win32con.IMAGE_ICON, 0, 0, icon_flags)
flags = NIF_ICON | NIF_MESSAGE | NIF_TIP
nid = (self.hwnd, 0, flags, self.ID_TBAR, self.hicon,
self.title)
Shell_NotifyIcon(NIM_ADD, nid)
def OnDestroy(self, hwnd, msg, wparam, lparam):
nid = (self.hwnd, 0)
Shell_NotifyIcon(NIM_DELETE, nid)
PostQuitMessage(0) # Terminate the app.
def OnTaskbarNotify(self, hwnd, msg, wparam, lparam):
global WRITE
if lparam==win32con.WM_LBUTTONUP or
lparam==win32con.WM_LBUTTONDBLCLK or lparam==win32con.WM_RBUTTONUP:
menu = CreatePopupMenu()
AppendMenu(menu, win32con.MF_STRING, self.IDM_TOGGLE,
self.resume_pause_msg[WRITE])
AppendMenu(menu, win32con.MF_STRING, self.IDM_NEWLOG, 'New
Log')
AppendMenu(menu, win32con.MF_STRING, self.IDM_QUIT,
'Quit')
pos = GetCursorPos()
TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0],
pos[1], 0, self.hwnd, None)
return 1
def OnCommand(self, hwnd, msg, wparam, lparam):
global WRITE, NEW_LOG
id = LOWORD(wparam)
if id == self.IDM_TOGGLE:
WRITE = 1 - WRITE
elif id == self.IDM_NEWLOG:
NEW_LOG = 1
elif id == self.IDM_QUIT:
DestroyWindow(self.hwnd)
else:
raise ValueError('Unknown command: %s' % id)
# MAIN
if __name__ == '__main__':
if len(sys.argv) != 3:
print >> sys.stderr, 'usage: logd LOG_DIR PORT'
sys.exit(1)
# Start writer
writer = LogWriter(sys.argv[1])
writer.start()
# Start TCP server
logd = LoggerDaemon(int(sys.argv[2]))
logd.start()
# Start UI
w=MainWindow()
PumpMessages()
-----------------------------------------
More information about the Python-list
mailing list