#!/usr/bin/python # 12/17/06 20:29:25 EST -0500 # Made by Alex Brown ("uh oh spaghetti-o") # Licensed under the GPL version 2.0 __build_date__ = "Sun 17 Dec 2006 08:29:25 PM EST -0500" __program__ = "Popcorn" __version__ = "0.0.2" __filecall__ = "popcorn.py" __author__ = "Alex Brown (\"uh oh spaghetti-o\") " __contributors__ = ["Natan 'whatah' Zohar"] __tagline__ = "is a python module to create awesome popups" __tagline2__ = "A python module to create awesome popups" __usage__ = __filecall__ + " [OPTIONS]\n" + __program__ + ' ' + __tagline__ __doc__ = __program__ + ", by " + __author__ + " Version " + __version__ + ', ' + __tagline__ __copyright__ = __program__ + " - " + __tagline2__ + "\nCopyright (C) 2006 Alex Brown" __license__ = "\t" + __program__ + ' - ' + __tagline2__ + """ Copyright (C) 2006 Alex Brown This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ # TODO: # * Set color of window border # * Set color of window # * Kill timeout after windows are destroyed # * Slide windows? # * function for rounding windows # * make if rounding = 0 or None, then connect the resize to like... blank import pygtk pygtk.require('2.0') import gtk, gobject, cairo import os, math deb = False def debug(*arg): """If debug is turned on, then print debug messages""" global deb; if deb == True: a = "" for i in arg: a += str(i) print a class Popup: def __init__(self, *args): self.reg = {} self.num = [] # Shorthand for functions """ self.show = self.appear self.popup = self.notification self.main_quit = self.quit """ def notification(self, name, subject, message, picture=None,formatting=True, timeout=5.0, rounding=0, padding=1): """ notification(name, subject, message, picture=None, formatting=True, timeout=5.0, rounding=0, padding=1) -> None name - name to identify the popup with subject - text for the subject/header message - body text picture - a local path to a picture file, gtk.gdk.Pixbuf, stock icon, or theme icon formatting - if True, then pango formatting/parsing will be used on the body text (see http://developer.gnome.org/doc/API/2.0/paango/PangoMarkupFormat.html) timeout - specify the time in seconds to show the popup (accepts decimals for miliseconds and such), if equal to 0, then the popup is shown infinetly rounding - how much to round the window corners padding - how much to pad the windows to give the feel of "separation" """ if timeout > 10 or timeout < 0: print "**popcorn: Popup.notification(timeout = 5.0) can not be more than 10.0 or less than 0.0" return elif timeout == None: timeout = 0 # Make the window a popup (ie, no window borders or anything) window = gtk.Window(gtk.WINDOW_POPUP) # Try and register window if self._register(name, window) == False: print "**popcorn: Unable to create '" + name + "'" return # Set window properties window.set_title("") # set a blank window title window.set_border_width(5) # set the border width, 5 pixels of padding window.timeout = timeout # store timeout with the window window.formatting = formatting # store formatting with the window window.set_resizable(False) # window is not resizeable window.connect('size-allocate', self._size_allocate_cb, name, rounding, padding) # special resizing method for nice curves # Create the boxes vbox = gtk.VBox(homogeneous=False, spacing=5) hbox1 = gtk.HBox(homogeneous=False, spacing=5) hbox2 = gtk.HBox(homogeneous=False, spacing=5) vbox.pack_start(child=hbox1, expand=True, fill=True, padding=0) vbox.pack_end(child=hbox2, expand=True, fill=True, padding=0) window.add(vbox) subject_label = gtk.Label(subject) message_label = gtk.Label(message) if formatting == True: message_label.set_use_markup(True) if picture != None: picture_image = gtk.Image() if os.path.exists(picture): picture_image.set_from_file(picture) elif type(picture) == 'gtk.gdk.Pixbuf': picture_image.set_from_pixbuf(picture) elif gtk.stock_lookup(picture) != None: picture_image.set_from_stock(picture) elif gtk.IconTheme().has_icon(picture) == True: picture_image.set_from_icon_set(picture) else: print "**popcorn: " + picture + " is not a valid filename, stock icon, or pixbuf" picture = None close_button = gtk.Button() close_icon = gtk.image_new_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) close_button.add(close_icon) close_button.set_relief(gtk.RELIEF_NONE) close_button.connect("clicked", self._callback_close, name) hbox1.pack_start(child=subject_label, expand=True, fill=True, padding=0) hbox1.pack_end(child=close_button, expand=False, fill=False, padding=0) if picture != None: hbox2.pack_start(child=picture_image, expand=True, fill=True, padding=0) hbox2.pack_start(child=message_label, expand=True, fill=True, padding=0) # Add a timeout for the popup window if specified (timeout > 0) if timeout > 0: gobject.timeout_add(int(timeout * 1000), self._callback_timeout, name) def _callback_close(self, widget, key): """Callback to close a notification""" self._close(key=key) return True def _callback_timeout(self, key_d): self._close(key=key_d) return False def _close(self, key): #print self.num #for i in self.num: # print i print 'key:',key window = self._get_notification(name=key) number = self._get_number(notif=window) self.appear(show=False, name=key) self.num.pop(number) del self.reg[key] self.update() def _size_allocate_cb(self, obj, allocation, key, rounding, padding): window = self._get_notification(name=key) # original code by: Natan 'whatah' Zohar w,h = allocation.width, allocation.height width, height = h,w bitmap = gtk.gdk.Pixmap(None, w, h, 1) cr = bitmap.cairo_create() # Clear the bitmap # Clear the bitmap cr.set_source_rgb(0,0,0) cr.set_operator(cairo.OPERATOR_DEST_OUT) cr.paint() cr.set_operator(cairo.OPERATOR_OVER) # Draw our shape into the pixmap using cairo # Let's try drawing a rectangle with rounded edges. padding = padding # Padding from the edges of the window rounded = rounding # How round to make the edges cr.set_source_rgb(0,0,0) # Move to top corner cr.move_to(0+padding+rounded, 0+padding) # Top right corner and round the edge cr.line_to(w-padding-rounded, 0+padding) cr.arc(w-padding-rounded, 0+padding+rounded, rounded, math.pi/2, 0) # Bottom right corner and round the edge cr.line_to(w-padding, h-padding-rounded) cr.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2) # Bottom left corner and round the edge. cr.line_to(0+padding+rounded, h-padding) cr.arc(0+padding+rounded, h-padding-rounded, rounded, math.pi+math.pi/2, math.pi) # Top left corner and round the edge cr.line_to(0+padding, 0+padding+rounded) cr.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi/2, 0) # Fill in the shape. cr.fill() # Set the window shape window.shape_combine_mask(bitmap, 0, 0) def _register(self, key, window_object): try: self.reg[key] = window_object self.num.append(window_object) window_object.order = len(self.num)-1 except: return False else: return True def update(self): #try: for i in range(len(self.num)): self.num[i].order = i self.position(number=self.num[i].order) #except: # return False #else: # return True def _get_notification(self, name=None, number=None): """ _get_notification(name=None, number=None) -> gtk.Window() name -- the name of the notification to return number -- the number of the notification to return * name and number cannot both be set """ if name == None and number == None: print "**popcorn: Popup.get_notification(): argument name or number was not given" return elif name != None and number != None: print "**popcorn: Popup.get_notification(): argument name and number cannot both be set" return if name != None: if self.reg.has_key(name) == True: window = self.reg[name] else: print "**popcorn: Popup.get_notification(): could not find key '" + str(name) + "' in Popup.reg" return elif number != None: if len(self.num) >= (number+1): window = self.num[number] else: print "**popcorn: Popup.get_notification(): could not find key '" + str(number) + "' in Popup.num" return return window def _get_number(self, notif = None, name = None): if notif == None and name == None: print "**popcorn: Popup.get_number(): argument notif or name was not given" return elif notif != None and name != None: print "**popcorn: Popup.get_number(): argument name and number cannot both be set" return if notif != None: for i in range(len(self.num)): if self.num[i] == notif: number = i print 'found', notif, 'in index',i,'of self.num' break else: print "**popcorn: Popup.get_number(): could not find window object '" + str(notif) + "' in Popup.num" return elif name != None: if self.reg.has_key(name) == True: window = self.reg[name] number = self.get_number(notif=window) else: print "**popcorn: Popup.get_number(): could not find key '" + str(number) + "' in Popup.num" return return number def position(self, name = None, number = None): window = self._get_notification(name = name, number = number) maxx = gtk.gdk.screen_width() # get the height of the screen maxy = gtk.gdk.screen_height() # get the width of the screen (width, height) = window.get_size() if window.order == 0: window.move(maxx-width, maxy-height) else: window.move(maxx-width, (self.num[window.order-1].get_position()[1] - height)) print "window.get_position():",window.get_position() def appear(self, show = True, name = None, number = None): """ appear(show=True, name=None, number=None) -> None show -- True if the given notification should be shown, False to hide it name -- Name of the notification to show/hide number -- Number of the window to show/hide * you cannot set name and number together """ window = self._get_notification(name = name, number = number) if window == None: print "**popcorn: Popup.appear(): Unable to get '" + name + "'" return if show == True: window.show_all() self.position(name=name, number=number) elif show == False: window.hide_all() #self.remove ###$$$ else: print "**popcorn: Popup.appear(): show can only be boolean (True or False)" def add_button(self): #"""Adds a button to the specified notification""" pass # not implemented yet def main(self, *args): """ main(*args) -> None Runs the gtk main loop """ gtk.main() def main_quit(self, *args): """ main_quit(*args) -> None Stops the gtk main loop """ gtk.main_quit() if __name__ == '__main__': #print "This program is used as a module, here is an example of what it does" n = Popup() n.notification(name='test', subject='Test', message='This is a test', picture='/tmp/pywooticon.ico', formatting=True, timeout=5.0) n.appear(name='test') n.notification(name='test2', subject='Test2', message='This is another test', picture=None, formatting=True, timeout=5.0) n.appear(name='test2') n.notification(name='test3', subject='Test3', message='This is another test with rounding!', picture=None, formatting=True, timeout=5.0, rounding=5, padding=1) n.appear(name='test3') try: n.main() except KeyboardInterrupt: try: n.main_quit() except: pass """ 'Noo, Save me!', cries he. o o=--- '-|-' /^ ^ """