From 6d70762f4a0f13d697e780391fa26d9dc6547b39 Mon Sep 17 00:00:00 2001 From: mfrasca <> Date: Mon, 21 May 2007 12:32:16 +0000 Subject: [PATCH] 1595052: writing strings I've added the class pywmgeneral.Drawable, offering an interface to pixmaps. this way a wmdocklib application can have more than one drawable: not only the main window, but as many pixmaps as are necessary. the first use of pywmgeneral.Drawable is to hold the graphical representation of the labels in an application. Labels are also new to the library and the intention is that they substitute the putString methods. Application.addLabel() is used to create a new named label. the label takes care of the specified area, or of the area necessary to show the specified string. Application.setLabelText() is used to specify a new text for a label. if necessary, a new pixmap is created. the area taken care of by a label cannot be modified. during the update cycle, labels that span an area which is too small for their text are scrolled a pixel at a time. pywmradio.py uses this functionality. --- examples/pywmradio.py | 9 +- wmdocklib/__init__.py | 2 +- wmdocklib/pywmgeneral.c | 172 ++++++++++++++++++++++++++++++++++++++- wmdocklib/pywmhelpers.py | 12 ++- wmdocklib/wmoo.py | 43 ++++++++++ 5 files changed, 227 insertions(+), 11 deletions(-) diff --git a/examples/pywmradio.py b/examples/pywmradio.py index d7aec68..4bf263b 100755 --- a/examples/pywmradio.py +++ b/examples/pywmradio.py @@ -53,7 +53,7 @@ class Application(wmoo.Application): globals()[radiodef[1]] = radiodef[2] pass else: - self.radioList.append( (radioname+' '*24, radiodef[1], radiodef[2]) ) + self.radioList.append( (radioname, radiodef[1], radiodef[2]) ) def handler(self, num, frame): @@ -106,7 +106,7 @@ class Application(wmoo.Application): def previousRadio(self, event): if self.currentRadio == 0: self.currentRadio = len(self.radioList) self.currentRadio -= 1 - self.putString(0, 10, self.radioList[self.currentRadio][0]) + self.setLabelText('name', self.radioList[self.currentRadio][0]) if self.child: self.stopPlayer() self.startPlayer() @@ -114,7 +114,7 @@ class Application(wmoo.Application): def nextRadio(self, event): self.currentRadio += 1 if self.currentRadio == len(self.radioList): self.currentRadio = 0 - self.putString(0, 10, self.radioList[self.currentRadio][0]) + self.setLabelText('name', self.radioList[self.currentRadio][0]) if self.child: self.stopPlayer() self.startPlayer() @@ -158,6 +158,7 @@ class Application(wmoo.Application): self.putPattern(54, 0, 3, 1, 52, 54-i) def update(self): + wmoo.Application.update(self) self._count += 1 if self._count <= 3: return @@ -285,7 +286,7 @@ def main(): background = background, patterns = patterns) # maxCharsPerLine = (width-2*xOffset) / char width - app.putString(0, 10, app.radioList[app.currentRadio][0]) + app.addLabel('name', (3, 13), (58, 10), app.radioList[app.currentRadio][0]) # app.addCallback(printevent) diff --git a/wmdocklib/__init__.py b/wmdocklib/__init__.py index ca4fe51..4102204 100644 --- a/wmdocklib/__init__.py +++ b/wmdocklib/__init__.py @@ -9,4 +9,4 @@ help(wmdocklib.) from pywmgeneral import * from pywmhelpers import * -__all__ = [] +__all__ = ['wmoo'] diff --git a/wmdocklib/pywmgeneral.c b/wmdocklib/pywmgeneral.c index de5b5ac..457dd42 100644 --- a/wmdocklib/pywmgeneral.c +++ b/wmdocklib/pywmgeneral.c @@ -30,6 +30,7 @@ */ #include +#include "structmember.h" #include #include #include @@ -254,10 +255,155 @@ static PyMethodDef PyWmgeneralMethods[] = { {NULL, NULL, 0, NULL} }; -void initpywmgeneral(void) { - Py_InitModule("pywmgeneral", PyWmgeneralMethods); +typedef struct { + PyObject_HEAD + /* Type-specific fields go here. */ + int has_drawable; + Pixmap drawable; + int width, height; +} drawable_DrawableObject; + +static PyObject * +Drawable_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + drawable_DrawableObject *self; + + self = (drawable_DrawableObject *)type->tp_alloc(type, 0); + if (self != NULL) { + self->has_drawable = 0; + self->width = 0; + self->height = 0; + } + + return (PyObject *)self; } +static int +Drawable_init(drawable_DrawableObject *self, PyObject *args, PyObject *kwds) +{ + unsigned int w, h; + if (! PyArg_ParseTuple(args, "ii", &w, &h)) + return -1; + if (!wmgen.attributes.depth) { + PyErr_SetString(PyExc_RuntimeError, "X client must be initialized first."); + return -1; + } + + if (self->has_drawable) + XFreePixmap(display, self->drawable); + self->has_drawable = 1; + self->width = w; + self->height = h; + self->drawable = XCreatePixmap(display, wmgen.pixmap, + w, h, wmgen.attributes.depth); + + return 0; +} + +static void +Drawable_dealloc(drawable_DrawableObject *self) +{ + if (self->has_drawable) + XFreePixmap(display, self->drawable); +} + +static PyObject * +Drawable_xCopyAreaToWindow(drawable_DrawableObject *self, PyObject *args, PyObject *kwds) +{ + unsigned int src_x, src_y, width, height, dst_x, dst_y; + if (! PyArg_ParseTuple(args, "iiiiii", &src_x, &src_y, &width, &height, &dst_x, &dst_y)) + return NULL; + + XCopyArea(display, self->drawable, wmgen.pixmap, NormalGC, + src_x, src_y, width, height, dst_x, dst_y); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Drawable_xClear(drawable_DrawableObject *self, PyObject *args, PyObject *kwds) +{ + XFillRectangle(display, self->drawable, NormalGC, + 0, 0, self->width, self->height); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +Drawable_xCopyAreaFromWindow(drawable_DrawableObject *self, PyObject *args, PyObject *kwds) +{ + unsigned int src_x, src_y, width, height, dst_x, dst_y; + if (! PyArg_ParseTuple(args, "iiiiii", &src_x, &src_y, &width, &height, &dst_x, &dst_y)) + return NULL; + + XCopyArea(display, wmgen.pixmap, self->drawable, NormalGC, + src_x, src_y, width, height, dst_x, dst_y); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMemberDef Drawable_members[] = { + {NULL} /* Sentinel */ +}; + +static PyMethodDef Drawable_methods[] = { + {"xCopyAreaFromWindow", (PyCFunction)Drawable_xCopyAreaFromWindow, METH_VARARGS, + "copy from the drawable to the global pixmap" + }, + {"xCopyAreaToWindow", (PyCFunction)Drawable_xCopyAreaToWindow, METH_VARARGS, + "copy from the global pixmap into the drawable" + }, + {"xClear", (PyCFunction)Drawable_xClear, METH_NOARGS, + "clears the pixmap" + }, + {NULL} /* Sentinel */ +}; + +static PyTypeObject drawable_DrawableType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pyywmgeneral.Drawable", /*tp_name*/ + sizeof(drawable_DrawableObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)Drawable_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "Drawable objects", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Drawable_methods, /* tp_methods */ + Drawable_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Drawable_init, /* tp_init */ + 0, /* tp_alloc */ + Drawable_new, /* tp_new */ +}; + /*****************************************************************************/ /* Original C sources (With some modifications) */ /*****************************************************************************/ @@ -299,6 +445,7 @@ static void GetXPM(XpmIcon *wmgen, char *pixmap_bytes[]) { XGetWindowAttributes(display, Root, &attributes); wmgen->attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions); + wmgen->attributes.depth = attributes.depth; err = XpmCreatePixmapFromData(display, Root, pixmap_bytes, &(wmgen->pixmap), &(wmgen->mask), &(wmgen->attributes)); @@ -639,3 +786,24 @@ void openXwindow(int argc, char *argv[], char *pixmap_bytes[], char *pixmask_bit XMoveWindow(display, win, wx, wy); } } + +#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ +#define PyMODINIT_FUNC void +#endif + +PyMODINIT_FUNC +initpywmgeneral(void) { + PyObject* m; + + drawable_DrawableType.tp_new = PyType_GenericNew; + if (PyType_Ready(&drawable_DrawableType) < 0) + return; + + m = Py_InitModule3("pywmgeneral", PyWmgeneralMethods, + "base C module for wmdocklib"); + if (m == NULL) + return; + + Py_INCREF(&drawable_DrawableType); + PyModule_AddObject(m, "Drawable", (PyObject *)&drawable_DrawableType); +} diff --git a/wmdocklib/pywmhelpers.py b/wmdocklib/pywmhelpers.py index 93adbd2..1ea50d5 100644 --- a/wmdocklib/pywmhelpers.py +++ b/wmdocklib/pywmhelpers.py @@ -77,7 +77,7 @@ def getCenterStartPos(s, areaWidth, offset): textArea = areaWidth - offset * 2 - 1 return (textArea - w) / 2 -def addChar(ch, x, y, xOffset, yOffset, width, height): +def addChar(ch, x, y, xOffset, yOffset, width, height, drawable=None): """Paint the character ch at position x, y in the window. Return the (width, height) of the character painted. (will be useful if @@ -109,17 +109,21 @@ def addChar(ch, x, y, xOffset, yOffset, width, height): chW = char_width if ch in "',.:;": chW = char_twidth - pywmgeneral.copyXPMArea(chX, chY, chW, char_height, targX, targY) + if drawable is None: + pywmgeneral.copyXPMArea(chX, chY, chW, char_height, targX, targY) + else: + drawable.xCopyAreaFromWindow(chX, chY, chW, char_height, targX, targY) return (chW, char_height) -def addString(s, x, y, xOffset, yOffset, width, height): +def addString(s, x, y, xOffset=0, yOffset=0, width=None, height=None, drawable=None): """Add a string at the given x and y positions. Call addChar repeatedely, so the same exception rules apply.""" lastW = 0 for letter in s: w, h = addChar(letter, x + lastW, y, - xOffset, yOffset, width, height) + xOffset, yOffset, width, height, + drawable) lastW += w def getVertSpacing(numLines, margin, height, yOffset): diff --git a/wmdocklib/wmoo.py b/wmdocklib/wmoo.py index 7e00102..13a96bd 100644 --- a/wmdocklib/wmoo.py +++ b/wmdocklib/wmoo.py @@ -14,8 +14,10 @@ class Application: 'area': if the pointer is here, the event is considered, """ + self._elements = {} self._events = [] self._sleep = 0.1 + self._cycle = 0 self._offset_x = self._offset_y = 3 self._char_width, self._char_height = pywmhelpers.initPixmap(*args, **kwargs) @@ -31,7 +33,48 @@ class Application: pywmhelpers.copyXPMArea(sourceX, sourceY+64, width, height, targetX, targetY) + def addLabel(self, labelId, orig, size=None, text=None): + """a label is a tuple with a + text: string; mutable + viewport: (orig: int, int, size: int, int); inmutable + pixmap: drawable; not user mutable, large enough to contain the text + + if size is not given, it is inferred from text. + """ + if size is None: + size = (self._char_width * len(text), self._char_height) + pixmapwidth = self._char_width * len(text) + import pywmgeneral + labelPixmap = pywmgeneral.Drawable(pixmapwidth, self._char_height) + self._elements[labelId] = [orig, size, pixmapwidth, 0, labelPixmap] + self.setLabelText(labelId, text) + + def setLabelText(self, labelId, text): + """updates the drawable associated with labelId + """ + (orig_x,orig_y), (size_x, size_y), width, offset, pixmap = self._elements[labelId] + newwidth = self._char_width * len(text) + if newwidth > width: + import pywmgeneral + pixmap = pywmgeneral.Drawable(newwidth, self._char_height) + self._elements[labelId][4] = pixmap + self._elements[labelId][2] = newwidth + self._elements[labelId][3] = 0 + pixmap.xClear() + pywmhelpers.addString(text, 0, 0, drawable=pixmap) + pixmap.xCopyAreaToWindow(0, 0, size_x, size_y, orig_x, orig_y) + def update(self): + for labelId in self._elements: + (orig_x,orig_y), (size_x, size_y), width, offset, pixmap = self._elements[labelId] + if size_x < width: + pixmap.xCopyAreaToWindow(offset, 0, size_x, size_y, orig_x, orig_y) + if offset == width: + offset = -size_x + else: + offset += 1 + self._elements[labelId][3] = offset + pass def redraw(self):