MS Windows Clipboard

Alex Martelli aleaxit at yahoo.com
Sat Oct 28 18:40:49 EDT 2000


"Paul Moore" <paul.moore at uk.origin-it.com> wrote in message
news:33r5OSRm=gidrgnmJUqLe=tsbYNP at 4ax.com...
    [snip]
> While we're on the subject, I recall from Perl's clipboard module a
> function which waits for the clipboard to change. The Python module
> doesn't have this, and I'm not sure how I would implement it. (It's

The architecture you sketch below is just fine, I think, if
your problem-statement is complete and accurate.

> not the sort of thing which is needed very often, I guess, but I have
> a specific application which would be cleaner with it...) I think I

What doesn't happen often at all is to want to wait indefinitely
and for just one event.  Normally, one wants some timeout, and
multiple events can be of interest to terminate the wait.  The
former, at least, is easy to obtain; the latter is subtler to get
in full generality, IMHO, exactly because the variations are so
many (and include both WM_whatevers you could receive, and
kernel-objects of various kinds becoming signaled -- possibly
COM events you may be monitoring, too).

Turning all kinds of 'things I may be waiting for' into one kind
is probably the cleanest general solution (and using a further,
_artificial_ 'kind' is a choice that has its advantages -- 'event
objects' can carry all sort of type-information and rich data --
particularly when much of the application can be written in
scripting languages; it's the choice we've recently taken in
reorganizing our own big app) -- but we're talking about a
few thousand lines of pretty subtle code, and it needs full
control of the application's event-loop.

So, anyway, back to the simple case...:

> need a clipboard viewer, with a hidden window to receive notification
> messages. But it sounds quite messy to implement. Two questions -

Not really.  It _is_ quite a lot of code, actually.

> first, are all the pieces available in win32all to implement this

Yes.  But generating a window and hooking its messages may
need a bit more 'buy-in' into Pythonwin than you might like,
although it's surely doable.

> myself, and second, would it be worth adding a method like this to
> win32clipboard?

Depends on the generality one can reach without excessive
effort, I guess.  Here, as an alternative, is a stand-alone VC++
implementation which I hacked together by modifying a
component I had once written to turn clipboard changes into
COM events (removing the COM stuff, adding the Python
interface, and a simple event-loop with timeout...).  You can
save it to clipwait.cpp, and build and install clipwait.pyd with
the following setup.py:

-- start setup.py
from distutils.core import setup, Extension

setup (name = "clipwait",
       version = "1.0",
       author = "Alex Martelli",
       author_email = "aleaxit at yahoo.com",
       description = "Wait for clipboard change (Win32 only)",

       ext_modules = [
         Extension('clipwait',
                   sources = ['clipwait.cpp',
                         ],
            )
       ]
)
-- finis setup.py

-- start clipwait.cpp
// clipwait -- wait for a clipboard change (Win32 only)
// version 1.0, A. Martelli 2000/10/28, aleaxit at yahoo.com
#include <Python.h>

#include <atlbase.h>
CComModule _Module;
#include <atlwin.h>

// global variables
HINSTANCE hinst;
HWND hPrev;
int nCbcs;

// DLL startup
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
{
    if(fdwReason==DLL_PROCESS_ATTACH) hinst=hinstDLL;
    return TRUE;
}

// ATL-derived window implementation class for clipboard-watching
class Csiwin: public CWindowImpl<Csiwin>
{
BEGIN_MSG_MAP(Csiwin)
    MESSAGE_HANDLER(WM_DRAWCLIPBOARD, OnDrawCb)
    MESSAGE_HANDLER(WM_CHANGECBCHAIN, OnCcbc)
END_MSG_MAP()

LRESULT OnDrawCb(UINT msg, WPARAM wp, LPARAM lp, BOOL&)
{
    ++nCbcs;
    if(hPrev) return SendMessage(hPrev, msg, wp, lp);
    else return 0;
}

LRESULT OnCcbc(UINT msg, WPARAM wp, LPARAM lp, BOOL&)
{
    if(hPrev == HWND(wp)) hPrev = HWND(lp);
    else if(hPrev) return SendMessage(hPrev, msg, wp, lp);
    else return 0;
}

};

// Python entry point
static PyObject*
clipwait(PyObject* self, PyObject* args)
{
    int timeout_msec = 5000;
    int nChanges = 2;

    if(PyArg_ParseTuple(args, "|ii", &timeout_msec, &nChanges)) {
        // setup the clipboard-viewer object, &c
        _Module.Init(0,hinst);
        Csiwin siwin;
        siwin.Create(0, CWindow::rcDefault, "", WS_OVERLAPPEDWINDOW);
        UINT timid = siwin.SetTimer(-1, 20);
        nCbcs=0;
        hPrev=siwin.SetClipboardViewer();
        DWORD endtime = timeout_msec + GetTickCount();

        // message-loop, with timeout & conditional exit
        MSG msg;
        while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
            if(nCbcs>=nChanges ||
                (timeout_msec && GetTickCount()>=endtime))
                    break;
        }

        // remove stuff & terminate
        siwin.KillTimer(timid);
        siwin.ChangeClipboardChain(hPrev);
        siwin.DestroyWindow();
        _Module.Term();
        return Py_BuildValue("i", nCbcs);
    } else {
        return 0;
    }
}

// Module functions
static PyMethodDef methods[] = {
  {"wait", clipwait, METH_VARARGS},
  {NULL, NULL}
};

// Module init function
extern "C"
void initclipwait()
{
    Py_InitModule("clipwait", methods);
}
-- finis clipwait.cpp

After an "import clipwait", the function clipwait.wait becomes
available; it takes 2 optional arguments -- a timeout value in
milliseconds (default 5000; pass 0 for "no timeout", i.e.,
infinite wait) and a number-of-clipboard-changes-to-wait-for
(default 2, as a first 'clipboard-change' event is normally
generated at once when the clipboard viewer is installed --
unless, I believe, the clipboard is originally empty -- maybe
I should check for that, now that I think of it... oh well,
that's for forthcoming version 1.1, I guess!-).

It returns the number of clipboard changes it has observed
(it will be the requested number, unless the timeout has
intervened).  Note the timeout is approximate, not precise
(at least 20 milliseconds' approximation, or worse).


Alex






More information about the Python-list mailing list