From pykde Thu May 27 15:22:13 2004 From: Torsten Marek Date: Thu, 27 May 2004 15:22:13 +0000 To: pykde Subject: Re: [PyKDE] Systray icons in PyQt Message-Id: <40B607A5.3080305 () gmx ! net> X-MARC-Message: https://marc.info/?l=pykde&m=108567147000197 MIME-Version: 1 Content-Type: multipart/mixed; boundary="--------------enigBFB37B886A023D4B7CEA6E90" This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enigBFB37B886A023D4B7CEA6E90 Content-Type: multipart/mixed; boundary="------------050709080505030508090307" This is a multi-part message in MIME format. --------------050709080505030508090307 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit Hello, 2nd version of ctypes code and Python extension module with one func only. The spec can be found here http://freedesktop.org/Standards/systemtray-spec/systemtray-spec-0.1.html, the code itself is taken from qjackctl (qjackctl.sf.net). When the code has undergone some review, I'll put it into the wiki. Should it be distributed somewhere else? Maybe it could go into the PyQt examples directory? greetings Torsten -- Torsten Marek ID: A244C858 -- FP: 1902 0002 5DFC 856B F146 894C 7CC5 451E A244 C858 www.keyserver.net -- wwwkeys.eu.pgp.net --------------050709080505030508090307 Content-Type: text/x-python; name="SystrayIcon.py" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="SystrayIcon.py" import qt import ctypes as c class SystrayIcon(qt.QLabel): """On construction, you have to supply a QPixmap instance holding the application icon. The pixmap should not be bigger than 32x32, preferably 22x22. Currently, no check is made. The class can emits two signals: Leftclick on icon: activated() Rightclick on icon: contextMenuRequested(const QPoint&) """ def __init__(self, icon, parent = None, name = ""): qt.QLabel.__init__(self, parent, name, qt.Qt.WMouseNoMask | qt.Qt.WRepaintNoErase | qt.Qt.WType_TopLevel | qt.Qt.WStyle_Customize | qt.Qt.WStyle_NoBorder | qt.Qt.WStyle_StaysOnTop) self.setMinimumSize(22, 22); self.setBackgroundMode(qt.Qt.X11ParentRelative) self.setBackgroundOrigin(qt.QWidget.WindowOrigin) libX11 = c.cdll.LoadLibrary("/usr/X11R6/lib/libX11.so") # get all functions, set arguments + return types XDefaultScreenOfDisplay = libX11.XDefaultScreenOfDisplay XDefaultScreenOfDisplay.argtypes = [c.c_void_p] XDefaultScreenOfDisplay.restype = c.c_void_p XScreenNumberOfScreen = libX11.XScreenNumberOfScreen XScreenNumberOfScreen.argtypes = [c.c_void_p] XInternAtom = libX11.XInternAtom XInternAtom.argtypes = [c.c_void_p, c.c_char_p, c.c_int] XGrabServer = libX11.XGrabServer XGrabServer.argtypes = [c.c_void_p] XGetSelectionOwner = libX11.XGetSelectionOwner XGetSelectionOwner.argtypes = [c.c_void_p, c.c_int] XSelectInput = libX11.XSelectInput XSelectInput.argtypes = [c.c_void_p, c.c_int, c.c_long] XUngrabServer = libX11.XUngrabServer XUngrabServer.argtypes = [c.c_void_p] XFlush = libX11.XFlush XFlush.argtypes = [c.c_void_p] class data(c.Union): _fields_ = [("b", c.c_char * 20), ("s", c.c_short * 10), ("l", c.c_long * 5)] class XClientMessageEvent(c.Structure): _fields_ = [("type", c.c_int), ("serial", c.c_ulong), ("send_event", c.c_int), ("display", c.c_void_p), ("window", c.c_int), ("message_type", c.c_int), ("format", c.c_int), ("data", data)] XSendEvent = libX11.XSendEvent XSendEvent.argtypes = [c.c_void_p, c.c_int, c.c_int, c.c_long, c.c_void_p] XSync = libX11.XSync XSync.argtypes = [c.c_void_p, c.c_int] dpy = int(qt.qt_xdisplay()) trayWin = self.winId(); iscreen = XScreenNumberOfScreen(XDefaultScreenOfDisplay(dpy)) # get systray window (holds _NET_SYSTEM_TRAY_S atom) selectionAtom = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S%i" % iscreen, 0) XGrabServer(dpy) managerWin = XGetSelectionOwner(dpy, selectionAtom) if managerWin != 0: # set StructureNotifyMask (1L << 17) XSelectInput(dpy, managerWin, 1L << 17) XUngrabServer(dpy); XFlush(dpy); if managerWin != 0: # send "SYSTEM_TRAY_OPCODE_REQUEST_DOCK to managerWin k = data() k.l = (0, # CurrentTime 0, # REQUEST_DOCK trayWin, # window ID 0, # empty 0) # empty ev = XClientMessageEvent(33, #type: ClientMessage 0, # serial 0, # send_event dpy, # display managerWin, # systray manager XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", 0), # message type 32, # format k) # message data XSendEvent(dpy, managerWin, 0, 0, c.addressof(ev)) XSync(dpy, 0) self.setPixmap(icon) self.setAlignment(qt.Qt.AlignHCenter) if parent: qt.QToolTip.add(self, parent.caption()) def setTooltipText(self, text): qt.QToolTip.add(self, text) def mousePressEvent(self, e): if e.button() == qt.Qt.RightButton: self.emit(qt.PYSIGNAL("contextMenuRequested(const QPoint&)"), (e.globalPos(),)) elif e.button() == qt.Qt.LeftButton: self.emit(qt.PYSIGNAL("activated()"), ()) --------------050709080505030508090307 Content-Type: text/x-csrc; name="traywin.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="traywin.c" #include #include #include /* System Tray Protocol Specification opcodes. */ #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 static PyObject* setTrayIcon(PyObject* self, PyObject* args) /*Display *dpy, Window trayWin*/ { /* System Tray Protocol Specification. */ Display *dpy; Window trayWin; PyArg_ParseTuple(args, "ll", &dpy, &trayWin); Screen *screen = XDefaultScreenOfDisplay(dpy); int iScreen = XScreenNumberOfScreen(screen); char szAtom[32]; snprintf(szAtom, sizeof(szAtom), "_NET_SYSTEM_TRAY_S%d", iScreen); Atom selectionAtom = XInternAtom(dpy, szAtom, False); XGrabServer(dpy); Window managerWin = XGetSelectionOwner(dpy, selectionAtom); if (managerWin != None) XSelectInput(dpy, managerWin, StructureNotifyMask); XUngrabServer(dpy); XFlush(dpy); if (managerWin != None) { XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = managerWin; ev.xclient.message_type = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ev.xclient.format = 32; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK; ev.xclient.data.l[2] = trayWin; ev.xclient.data.l[3] = 0; ev.xclient.data.l[4] = 0; XSendEvent(dpy, managerWin, False, NoEventMask, &ev); XSync(dpy, False); } return Py_None; } static char traywin_docs[] = "setTrayIcon: make the window WID a tray icon\n"; static PyMethodDef traywin_funcs[]= { {"setTrayIcon", (PyCFunction)setTrayIcon, METH_VARARGS, traywin_docs}, {NULL} }; void inittraywin(void) { PyObject* thismod = Py_InitModule3("traywin", traywin_funcs, "empty"); } --------------050709080505030508090307 Content-Type: text/x-python; name="systray2.py" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="systray2.py" import qt import sys import traywin class SystrayIcon(qt.QLabel): def __init__(self, icon, parent = None, name = ""): qt.QLabel.__init__(self, parent, name, qt.Qt.WMouseNoMask | qt.Qt.WRepaintNoErase | qt.Qt.WType_TopLevel | qt.Qt.WStyle_Customize | qt.Qt.WStyle_NoBorder | qt.Qt.WStyle_StaysOnTop) self.setMinimumSize(22, 22); self.setBackgroundMode(qt.Qt.X11ParentRelative) self.setBackgroundOrigin(qt.QWidget.WindowOrigin) dpy = int(qt.qt_xdisplay()) winid = self.winId(); print "test" traywin.setTrayIcon(dpy, winid) self.setPixmap(icon) self.setAlignment(qt.Qt.AlignHCenter) if parent: qt.QToolTip.add(self, parent.caption()) def setTooltipText(self, text): qt.QToolTip.add(self, text) def mousePressEvent(self, e): if e.button() == qt.Qt.RightButton: self.emit(qt.PYSIGNAL("contextMenuRequested(const QPoint&)"), (e.globalPos(),)) elif e.button() == qt.Qt.LeftButton: self.emit(qt.PYSIGNAL("activated()"), ()) def showPopup(pos): popup.popup(pos) a = qt.QApplication(sys.argv) img = qt.QImage("/home/shlomme/bilder/rattlesnake_tray.png") pm = qt.QPixmap() pm.convertFromImage(img.smoothScale(22, 22), 0) w = SystrayIcon(pm) w.setTooltipText("Rattlesnake") popup = qt.QPopupMenu() popup.insertItem("This is amazing, but please quit now!", w.close) qt.QObject.connect(w, qt.PYSIGNAL("contextMenuRequested(const QPoint&)"), showPopup) qt.QObject.connect(a,qt.SIGNAL("lastWindowClosed()"),a,qt.SLOT("quit()")) w.show() a.exec_loop() --------------050709080505030508090307-- --------------enigBFB37B886A023D4B7CEA6E90 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.4 (GNU/Linux) Comment: Using GnuPG with Debian - http://enigmail.mozdev.org iD8DBQFAtgelfMVFHqJEyFgRAqthAJwMPMFqqG9iYIO46fRD0K6NHMkvjwCeNJFf Mp9CnlvOzU4jIvX9AUNN/1g= =v5ob -----END PGP SIGNATURE----- --------------enigBFB37B886A023D4B7CEA6E90-- _______________________________________________ PyKDE mailing list PyKDE@mats.imk.fraunhofer.de http://mats.imk.fraunhofer.de/mailman/listinfo/pykde