Hi

Please find attached my program (please excuse the mess) that uses
cairographics.org to draw on a win32 window using GDI. My question is:
What is the best event handling/drawing loop that one can have in
pywin32? Is for instance a message map faster or slower? I am new to
the win32 api and don't know what is the best way to do things.

Thank You,
Gerdus van Zyl
from win32api import *
try:
    from winxpgui import *
except ImportError:
    from win32gui import *
    
import win32con
import sys, os
import cairo
import thread
import time
from random import randint,random
from pprint import pprint

import win32clipboard as w 
import win32con
import timer

from uxGUI.platforms import uxBasePlatform
from uxGUI.uxBase import logging

def dword_to_rgb(color):
    r = (color >> 16) & 0xFF
    g = (color >> 8) & 0xFF
    b = color & 0xFF
 
    return (r, g, b)

def RGBToHTMLColor(rgb_tuple):
    """ convert an (R, G, B) tuple to #RRGGBB """
    hexcolor = '#%02x%02x%02x' % rgb_tuple
    # that's it! '%02x' means zero-padded, 2-digit hex values
    return hexcolor


#InitCommonControls()

#import EasyDialogs

import uxGUI.platforms.EasyDialogs as EasyDialogs

#DefWindowProc

class platformWindow(uxBasePlatform):
    def __init__(self,uxWindow):
        #self.Render is a callback to request a redraw
        #self.bubblemark = Bubblemark(self.Render)
        
        uxBasePlatform.__init__(self,uxWindow)
        
        
        self._buffer = None
        
        self.lastrender = time.clock()
        self.frames = 0       
        self.renderfps = 0.0
        
        
        self.event_timer = None
        
        message_map = {
                win32con.WM_DESTROY: self.OnDestroy,
                win32con.WM_PAINT: self.OnPaint,
                win32con.WM_SETCURSOR: self.OnCursor,
                win32con.WM_ERASEBKGND: self.OnBackgroundErase,
                win32con.WM_LBUTTONDOWN: self.OnClick,
                win32con.WM_RBUTTONDOWN: self.OnClickR,
                win32con.WM_KEYDOWN: self.OnKey,
                win32con.WM_MOUSEMOVE: self.OnMouseMove,
                win32con.WM_LBUTTONUP: self.OnMouseUp,
                win32con.WM_SIZE: self.onSize,
                win32con.WM_CHAR: self.onChar,
                #win32con.WM_NCHITTEST: DefWindowProc,
                #win32con.WM_PRINT: self.OnPrint,
                win32con.WM_PRINTCLIENT: self.OnPrint,
                win32con.WM_NCHITTEST: self.onChi,
                
                
                #win32con.WM_SHOWWINDOW: self.onShowWindow,
                win32con.WM_CLOSE: self.onClose,
                #win32con.WM_TIMER: self.onTimer,
                #win32con.WM_COMMAND: self.OnCommand,
                #win32con.WM_USER+20 : self.OnTaskbarNotify,
                # owner-draw related handlers.
                #win32con.WM_MEASUREITEM: self.OnMeasureItem,
                #win32con.WM_DRAWITEM: self.OnDrawItem,
        }
        
        #message_map.setdefault(DefWindowProc)
        # Register the Window class.
        wc = WNDCLASS()
        hinst = wc.hInstance = GetModuleHandle(None)
        self.hinst = hinst
        wc.lpszClassName = "uxPythonWindow"
        wc.hIcon       = LoadIcon(0, win32con.IDI_INFORMATION)
        #wc.hIcon       = LoadIcon(hinst, 0)
        wc.lpfnWndProc = message_map # could also specify a wndproc.        
        classAtom = RegisterClass(wc)
        # Create the Window.
        #style = win32con.WS_THICKFRAME | win32con.WS_VISIBLE
        #style = win32con.WS_THICKFRAME | win32con.WS_MAXIMIZEBOX | win32con.WS_MINIMIZEBOX | win32con.WS_SYSMENU | win32con.WS_VISIBLE
        style = win32con.WS_THICKFRAME | win32con.WS_MAXIMIZEBOX | win32con.WS_MINIMIZEBOX | win32con.WS_SYSMENU
#        self.hwnd = CreateWindow( classAtom, "Bubblemark", style, \
#                0, 0, win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT, \
#                0, 0, hinst, None)
        self.hwnd = CreateWindow( classAtom, "Window", style, \
                0, 0, self.width, self.height, \
                0, 0, hinst, None)
        UpdateWindow(self.hwnd)
        
        
        timer.set_timer(500, self.onTimer)
        
        timer.set_timer(1000, lambda id,time: self.calcfps() )
        #not in pywin32
        #SetTimer(self.hwnd, 415, 0, None)          
        
        #ownSetTimer(0,self.OnTimer)      
        
        #self.bubblemark.calc()
        
        #self._start()
        
        self.setIcon()
        
    def getsyscolour(self,ref):
        
        #http://www.newobjects.com/pages/ndl/alp/af-sysColor.htm
        #css: http://www.iangraham.org/books/xhtml1/appd/update-23feb2000.html
        rm = {}
        rm['window'] = win32con.COLOR_BTNFACE
        rm['text'] = win32con.COLOR_BTNTEXT
        
        if ref in rm.keys():
            winkey = rm[ref]
            wc=  GetSysColor(winkey)            
            wwc = list(dword_to_rgb(wc))
            #print type(wwc)            
            wwc[0],wwc[2] = wwc[2],wwc[0]
            wwc = tuple(wwc)
            webc = RGBToHTMLColor(wwc)
        else:
            return uxBasePlatform.getsyscolour(self,ref)

        #print 'win32:',ref,wc,wwc,webc
        
        return webc    

    def _start(self):        
        print "_start"
        #thread.start_new_thread(self._runfpscalc,())
        #thread.start_new_thread(self._timer,())
        
        #UpdateWindow(self.hwnd)  
        #InvalidateRect(self.hwnd, None, True)
        
        #AnimateWindow(self.hwnd,250,win32con.AW_SLIDE | win32con.AW_HOR_POSITIVE | win32con.AW_ACTIVATE)
        
        #AnimateWindow(self.hwnd,250,win32con.AW_BLEND | win32con.AW_ACTIVATE)
        #SetWindowLong(self.hwnd, win32con.GWL_EXSTYLE,GetWindowLong(self.hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED)
              
        ShowWindow(self.hwnd,win32con.SW_SHOWNORMAL)
        
        #InvalidateRect(self.hwnd, None, True)
                
        return None
    
    def onClose(self, hWnd, msg, wParam, lparam):
        #AnimateWindow(self.hwnd,250,win32con.AW_BLEND|win32con.AW_HIDE)
        #ShowWindow(self.hwnd,win32con.SW_HIDE)
        PostMessage(self.hwnd, win32con.WM_DESTROY, 0, 0)
        return True
    
    def close(self):
        PostMessage(self.hwnd, win32con.WM_DESTROY, 0, 0)
    
    def onTimer(self, id, time):
        #print self.event_timer
        if self.event_timer != None:
            self.event_timer(self.renderfps)   
            #time.sleep(10)
            #time.sleep(0.001)    
            
    def setIcon(self):
        #print sys.executable
        #print __file__
        #return
        #hIcon = LoadIcon(self.hinst, 0)
        
        #hIcon = ExtractIconEx("C:\uxpython\exe\py2exe\dist\ide1.exe",0)
        try:
            hIcon = ExtractIconEx(sys.executable,0)
            #hIcon = ExtractIconEx("C:\uxpython\exe\py2exe\dist\minimal.exe",0)
            if hIcon == ([], []):
                raise
            #print hIcon
        except:
            logging.warning('Icon loading failed - '+ sys.executable)
            return
        large = hIcon[0][0]
        small = hIcon[1][0]
        #hIcon = small
        #print hIcon
        #hIcon = LoadImage(0, "myicon.ico", win32con.IMAGE_ICON, 16, 16, win32con.LR_LOADFROMFILE)
        SendMessage(self.hwnd, win32con.WM_SETICON, win32con.ICON_SMALL, small)
        SendMessage(self.hwnd, win32con.WM_SETICON, win32con.ICON_BIG, large)
        
        #hIcon = LoadIcon(0, win32con.IDI_QUESTION)
        #hIcon = LoadIcon(self.hinst, 0)
        #SendMessage(self.hwnd, win32con.WM_SETICON, win32con.ICON_BIG, hIcon)
        pass
        #ic = LoadImage(0,win32con.IDI_WARNING,win32con.IMAGE_ICON,32,32,win32con.LR_DEFAULTSIZE)
        #ics = LoadImage(0,win32con.IDI_APPLICATION,win32con.IMAGE_ICON,16,16,win32con.LR_DEFAULTSIZE)
        #ics = LoadIcon(0,win32con.IDI_APPLICATION)
                
    def getClipboardText(self): 
        w.OpenClipboard() 
        d=w.GetClipboardData(win32con.CF_TEXT) 
        w.CloseClipboard() 
        return d 
     
    def setClipboardText(self,aString): 
        aString = str(aString)
        aType = win32con.CF_TEXT
        w.OpenClipboard()
        w.EmptyClipboard()
        w.SetClipboardData(aType,aString) 
        w.CloseClipboard()                 
                
            

    
    def transparency(self,perc=70):
        SetWindowLong(self.hwnd, win32con.GWL_EXSTYLE,GetWindowLong(self.hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED)
        SetLayeredWindowAttributes(self.hwnd, 0, (255 * perc) / 100, win32con.LWA_ALPHA);
        
        #AW_SLIDE/AW_BLEND
        #AnimateWindow(self.hwnd,200,win32con.AW_SLIDE | win32con.AW_HOR_POSITIVE)        

        
        
        #full opace
#        // Remove WS_EX_LAYERED from this window styles
#        SetWindowLong(hwnd, GWL_EXSTYLE,
#                GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
#        // Ask the window and its children to repaint
#        RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME |
#                RDW_ALLCHILDREN);        
                

                
            
    def onChar(self, hWnd, msg, wParam, lparam):        
        VK_SHIFT = win32con.VK_SHIFT
        VK_CONTROL = win32con.VK_CONTROL
        
        TAB = win32con.VK_TAB
        
        isControl = GetKeyState(VK_CONTROL) & 0xff00
        isControl = (not isControl == 0)   
        if isControl:            
            return     
        
        VK_ENTER = 13
        
        if wParam in [win32con.VK_BACK,VK_ENTER,TAB]:
            #Handled by keydown
            return
        
        ch = unichr(wParam)
            
        mods = list()
        if isControl:
            mods.append('control')        
        evt = [ch,mods]
        #print 'onChar:',evt
        self.uWindow.event_keyup(evt)             
        
    def OnKey(self, hWnd, msg, wParam, lparam):        
        VK_SHIFT = 16
        VK_LEFT = 37        
        VK_CONTROL = 17
        VK_ENTER = 13
        #pass
        #print event
        isShift = GetKeyState(VK_SHIFT) & 0xff00
        isShift = (not isShift == 0)
        
        isControl = GetKeyState(VK_CONTROL) & 0xff00
        isControl = (not isControl == 0)
        #print event,isControl
        keymap = {}
        keymap[37] = 'left'
        keymap[38] = 'up'
        keymap[39] = 'right'
        keymap[40] = 'down'
        keymap[36] = 'home'
        keymap[35] = 'end'
        keymap[46] = 'delete'        
        keymap[16] = 'shift'
        keymap[17] = 'control'
        keymap[win32con.VK_BACK] = 'backspace'
        keymap[VK_ENTER] = 'enter'      
        keymap[9] = 'tab'     
        
        if wParam in keymap:
            ch = keymap[wParam]
        else:
            if not isControl:
                return
            ch = chr(wParam)
            
        mods = list()
        if isShift:
            mods.append('shift')
        if isControl:
            mods.append('control')
        evt = [ch,mods]
        #print 'onKey',evt
        self.uWindow.event_keyup(evt)
            
    def OnMouseUp(self, hWnd, msg, wparam, lparam):
        x = LOWORD(lparam)
        y = HIWORD(lparam)        
        self.uWindow.event_mouseup(x,y,"L")
        
    def onChi(self, hWnd, msg, wparam, lparam):
        
        x = LOWORD(lparam)
        y = HIWORD(lparam)
        res = DefWindowProc(hWnd, msg, wparam, lparam)
        #ax = rc[0] - cr[0]
        
        #print "chi",x,y,res
        cm = {}
        cm[win32con.HTBOTTOM] = 'sizey'
        cm[win32con.HTLEFT] = 'sizex'
        cm[win32con.HTRIGHT] = 'sizex'
        cm[win32con.HTBOTTOMRIGHT] = 'sizeNW'
        cm[win32con.HTBOTTOMLEFT] = 'sizeNE'
        
        if res in cm.keys():
            self.cursor = cm[res]            
        else:
            #self.cursor = 'normal'
            pass
        
        return res
        
            
    def OnMouseMove(self, hWnd, msg, wparam, lparam):
        x = LOWORD(lparam)
        y = HIWORD(lparam)
        self.uWindow.event_mousemove(x,y)        
        
    def OnClick(self, hWnd, msg, wparam, lparam):        
        x = LOWORD(lparam)
        y = HIWORD(lparam)
        #print x,'v',y
        self.uWindow.event_mouseclick(x,y,"L")
        
    def OnClickR(self, hWnd, msg, wparam, lparam):        
        x = LOWORD(lparam)
        y = HIWORD(lparam)
        #print x,'v',y
        self.uWindow.event_mouseclick(x,y,"R")        
        

    def OnPrint(self, hWnd, msg, wparam, lparam):
        rc = GetClientRect(hWnd)
        cx = rc[0]
        cy = rc[1]
        cw = rc[2] - rc[0]
        ch = rc[3] - rc[1]
                
        surf = cairo.Win32Surface(wparam)
        ctx = cairo.Context(surf)
        
        self.uWindow.event_paint(ctx,[cx,cy,cw,ch])
        
        surf.finish()
           
        
    def OnPaint(self, hWnd, msg, wparam, lparam):   
        self.frames += 1     
        hdc, ps = BeginPaint(hWnd)
        #rc = GetClientRect(hWnd)
        rc = ps[2]
        #print rc
        #rc = GetUpdateRect(hWnd)
        #print GetUpdateRect(hWnd)
        left, top, right, bottom = rc            
        
        width = right - left
        height = bottom - top
        x = left
        y = top      
        
        cx = x
        cy = y
        cw = width
        ch = height
        
        #print x,y,width,height
        
        
        #_buffer = CreateCompatibleDC(hdc) 
        #Double Buffer Stage 1
        #hBitmap = CreateCompatibleBitmap(hdc,width,height)
        #hOldBitmap = SelectObject(_buffer, hBitmap )
        
        
        surf = cairo.Win32Surface(hdc)
        ctx = cairo.Context(surf)
        
        
        if self._buffer == None:                            
            surfb = surf.create_similar(cairo.CONTENT_COLOR,self.width,self.height)                
        else:
            surfb = self._buffer
            
        ctxb = cairo.Context(surfb)
        #fill with current window content 
        ctxb.set_source_surface(surf,0,0)
        ctxb.paint()
    
    
        ctxb.translate(-cx,-cy)
        
        #call uWindow to paint
        self.uWindow.event_paint(ctxb,[cx,cy,cw,ch])    
        
        ctx.set_source_surface(surfb,cx,cy)
        ctx.paint()          
        
        
        #ctx.set_source_rgb(0,0,0)
        #ctx.select_font_face("Arial",cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
        #ctx.set_font_size(10)    
            
        #txt = "Rendering FPS: " + str(self.renderfps)
        
        #ctx.reset_clip()
            
        #ctx.move_to(5,10)        
        #ctx.show_text(txt)
        #print txt
        
        #show redraw rectangles
        if False:        
            ctx.rectangle(cx,cy,cw,ch)
            ctx.set_source_rgb(255,0,0)
            ctx.stroke()
            
        
            
        surf.finish()
        
        #BitBlt(hdc,0, 0, width,  height,
        #  _buffer, x, y, win32con.SRCCOPY) 
        
        #SelectObject( _buffer, hOldBitmap ) 
        #DeleteObject( hBitmap ) 
        #DeleteDC( _buffer )               
        
        EndPaint(hWnd,ps)
        
        #self.bubblemark.renderfps = 1.0 / self.bubblemark.rendertime
        
    def OnCursor(self, hwnd, msg, wparam, lparam):
        
        if not hasattr(self, 'cursor'):
            return
        #IDC_HAND = 32649
        #IDC_ARROW
        am = {}
        am['normal'] = LoadCursor(0, win32con.IDC_ARROW)
        am['edit'] = LoadCursor(0, win32con.IDC_IBEAM)
        am['hand'] = LoadCursor(0, win32con.IDC_HAND)
        am['size'] = LoadCursor(0, win32con.IDC_SIZEWE)
        am['sizex'] = LoadCursor(0, win32con.IDC_SIZEWE)
        am['sizey'] = LoadCursor(0, win32con.IDC_SIZENS)
        am['sizexy'] = LoadCursor(0, win32con.IDC_SIZENWSE)
        
        am['sizeNE'] = LoadCursor(0, win32con.IDC_SIZENESW)
        am['sizeNW'] = LoadCursor(0, win32con.IDC_SIZENWSE)
        if self.cursor in am.keys():
            SetCursor(am[self.cursor])
            #print self.cursor
        else:        
            cur_normal = LoadCursor(0, win32con.IDC_ARROW)
            SetCursor(cur_normal)
        
    def OnBackgroundErase(self, hwnd, msg, wparam, lparam):
        return False

        
    def OnDestroy(self, hwnd, msg, wparam, lparam):
        #nid = (self.hwnd, 0)
        #Shell_NotifyIcon(NIM_DELETE, nid)
        #AnimateWindow(self.hwnd,250,win32con.AW_BLEND|win32con.AW_HIDE)
        print "Going Gone!..."
        PostQuitMessage(0) # Terminate the app. 
        print "Exited."
        
    def FileOpenDialog(self):
        tl = [('XML Files (*.xml)','*.xml'),
              ('All Files (*.*)', '*.*'), 
              ('C Files (*.c, *.h)', '*.c;*.h')]
        return EasyDialogs.AskFileForOpen(typeList=tl)
    
    def FileSaveDialog(self,msg,defaultn):
        return EasyDialogs.AskFileForSave(msg,defaultn)        
        
    def onSize(self, hWnd, msg, wparam, lparam):
        #rc = GetWindowRect(hWnd)        
        #rc = GetClientRect(hWnd)
        cr = GetClientRect(hWnd)
        #print rc        
        #print "onSize",w
        #print "rc",w
        cw = cr[2] - cr[0]
        ch = cr[3] - cr[1]
        
        
        self.width = cw
        self.height = ch
                
        self.uWindow.width = cw
        self.uWindow.height = ch
        
        
        self._buffer = None        
        
    def ResizeW(self):
        rc = GetClientRect(self.hwnd)        
        x,y,w,h = rc        
        ow = w
        
        wr = GetWindowRect(self.hwnd)
        cr = GetClientRect(self.hwnd)
        
        woff = (wr[2]-wr[0]) - (cr[2]-cr[0])
        
        
        #print w
        w = self.uWindow.width
        #print w,' vs ',self.width,' vss ',ow
        #print '--'*40
        #h = self.height        
        #print "ResizeW",w,'-',self.width
        if not w == self.width:        
            SetWindowPos(self.hwnd,0,x,y,w+woff,h,win32con.SWP_NOMOVE |win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER)
            self.width = w
        
    def ResizeH(self):
        rc = GetClientRect(self.hwnd)   
        x,y,w,h = rc        
        
        wr = GetWindowRect(self.hwnd)
        cr = GetClientRect(self.hwnd)
        
        hoff = (wr[3]-wr[1]) - (cr[3]-cr[1])        
        
        #w = self.width
        h = self.uWindow.height
        #print "ResizeH",h,'-',self.height
        
        if not h == self.height:
            SetWindowPos(self.hwnd,0,0,0,w,h+hoff,win32con.SWP_NOMOVE |win32con.SWP_NOACTIVATE | win32con.SWP_NOZORDER)
            self.height = h
        
    def invalidate(self,x,y,w,h):
        x = int(x)
        y = int(y)
        w = int(w)
        h = int(h)

        r = (x,y,x+w,y+h)
        #print 'r_',r        
        InvalidateRect(self.hwnd, r, False)
        
    def setCaption(self,caption):
        SendMessage(self.hwnd,win32con.WM_SETTEXT, 0, caption)
        
           
        
   
       
profile = False

if profile:
    import atexit
    import hotshot, hotshot.stats
    from bstats import * #http://code.google.com/p/msolo/wiki/bstats
    prof = hotshot.Profile("c:/uxperformance.prof")
    
    #prof.start()
    
    def interpretit():     
        print "closing..."
        prof.close()      
        fz = os.path.getsize("c:/uxperformance.prof")
        fz = fz / 1000000.0
        print "interpreting... ",fz,' MB'
                     
        #outliers
        bs = load('c:/uxperformance.prof')
        bs.print_top_items(FALLOUT2, 10)
        
        #print "extents"
        #bs.print_func_callees('c:\uxpython\src\uxgui\uxbase.py:387(extents)')
        #print "Reflow"
        #bs.print_func_callees('c:\uxpython\src\uxgui\uxpwindowapp.py:883(_doReflow)')

        #print "_invalidate"
        #bs.print_func_callees('c:\uxpython\src\uxgui\uxpwindowapp.py:469(_invalidate)')


        #return
        #print "after"
        #prof.stop()

        stats = hotshot.stats.load("c:/uxperformance.prof")
        print '='*80        

        #stats.strip_dirs()
        print "display1"        
        stats.sort_stats('time', 'calls')
        stats.print_stats(20)     
        print '- '*80
#        stats.sort_stats('cumulative','calls')
#        stats.print_stats(20)
        print '='*80
        
    
    atexit.register(interpretit)  
        
class platformApplication(object):
    def __init__(self):        
        self.windows = []
    def add(self,win):
        self.windows.append(win)
        
    def after(self):
        print "stopping.."
        for w in self.windows:
            w.unload()
            
            #if profile:
            #    pprint(w.stats)   
            
        print "exit."     
        
        
        
        #sys.exit(0)        
        
        
    def run(self):
        
        #platform Window
        for w in self.windows:
            w.pWindow._start()        
            
        #uxpython window
        for w in self.windows:
            print "[init]"
            thread.start_new_thread(w._start, ())
            #w._start()
            print "[init:done]"            
            

            
        print "run"
        #application = Application()
        
        if profile:            
            prof.start()

        PumpMessages()     
        
        if profile:
            prof.stop() 
            
        self.after()          


#*************************************************************************        
appInstance = platformApplication()
_______________________________________________
python-win32 mailing list
python-win32@python.org
http://mail.python.org/mailman/listinfo/python-win32

Reply via email to