MS Windows Clipboard

Alex Martelli aleaxit at yahoo.com
Mon Oct 30 05:54:08 EST 2000


"Alex Martelli" <aleaxit at yahoo.com> wrote in message
news:8ti9ca0tcj at news1.newsguy.com...
    [snip]
> Now, doing it in Python itself is not a bad idea, actually.  I suspect
> one could do it all with calldll, and with win32all it should be far
> easier.  Of course, if one's application does not otherwise need any
    [snip]

Well, here's an attempt at pretty-literal translation (from C++/ATL
to Python/win32all), but, there are some peculiarities...:


import win32ui, win32clipboard as clip, win32con, win32api, win32gui

hPrev = 0
nCbcs = 0

def onDrawCb(*a):
    global nCbcs
    nCbcs += 1
    # print "Draw %d" % nCbcs
    if hPrev: return win32api.SendMessage(hPrev,*a[-1][1:4])
    else: return 1

def onCcbc(*a):
    global hPrev
    if hPrev==a[-1][2]: hPrev=a[-1][3]
    elif hPrev: return win32api.SendMessage(hPrev,*a[-1][1:4])
    else: return 0

def wait(timeout_msec=5000, need_changes=1):
    global hPrev, nCbcs
    win=win32ui.CreateFrame()
    win.CreateWindow(None,'',win32con.WS_OVERLAPPEDWINDOW)
    win.HookMessage(onDrawCb,win32con.WM_DRAWCLIPBOARD)
    win.HookMessage(onCcbc,win32con.WM_CHANGECBCHAIN)
    try:
        hPrev=clip.SetClipboardViewer(win.GetSafeHwnd())
    except win32api.error, err:
        if err.args[0]: raise
    nCbcs=0
    if timeout_msec:
        timid = win.SetTimer(1,20)
        curtime = win32api.GetTickCount()
        endtime = timeout_msec + curtime

    while nCbcs<need_changes:
        win32gui.PumpWaitingMessages()
        if timeout_msec and win32api.GetTickCount()>endtime: break

    if timeout_msec:
        win.KillTimer(timid)
    clip.ChangeClipboardChain(win.GetSafeHwnd(), hPrev)
    win.DestroyWindow()
    return nCbcs


Decommenting the print statement within onDrawCb shows a
peculiar "stutter" -- it seems to be called _twice_ for
each clipboard change (plus, twice at the start if the
clipboard is non-empty at entry -- which is why I zero
out the nCbcs counter _after_ the SetClipboardViewer
that causes the first 2 calls).  I don't know why that
is -- no equivalent in the C++ version, no mention in
the docs either (that I can see).

The try/except around SetClipboardViewer is another
peculiarity, but a documented one: win32clipboard raises
a win32api.error if there was no previously set
clipboard-viewer (...?), so I must test for a 0
error-code in order to propagate only 'true' errors.

The *a arguments to the message-hook functions are
due to a documentation error (I think!): under the
docs for HookMessage, it's said that the handling
callable will be called with TWO arguments (a "handler
object", then a tuple of message-arguments).  In fact,
it seems only one object (the tuple) is being passed.

So, I'm taking whatever arguments are being passed,
and hoping the tuple-of-args is the last (or only)
one -- just in case the docs are in fact right and
it's a bug in the current win32all build (for sure,
either the docs or the win32all code will have to
be changed, but I don't know which, because I do not
understand what that 'handler object' is supposed to
be).

All in all, then, we're talking about 40+ lines of
code for Python (using win32all), versus 95 for C++
(using ATL) -- the latter includes a bit more
comments and some amount of Python-interfacing
code that the Python version doesn't need, though:-).

Looking specifically at the heart of the wait
function, it's 25 lines for C++ and 23 for Python --
basically just the difference in comments and
whitespace.  Makes sense, since we're programming
at just about the same level in either language.


I think this argues in favour of doing the
non-portable-anyway system-level programming
in the system-level language (C++), as the VHLL
buys you nothing much here, but, as this surely
isn't performance-critical code, I guess somebody
else might argue "might as well do it in Python"
instead:-).  However, the win32all/Pythonwin
framework appears to be somewhat less 'stable'
(considering documentation issues too, as well
as that mysterious 'stutter') than the ATL fw
I'm using in C++ -- particularly when coding at
system level, I'd rather have a clue about what
IS going on rather than take a purely empirical
"tweak until it seems to work" approach:-).


Alex






More information about the Python-list mailing list