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