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