From 585a6e69b31dd3bd318218da9821b34ccbe210dc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marcus=20Wikstr=F6m?= Date: Wed, 27 Jan 2010 15:03:54 +0200 Subject: [PATCH] Initial commit --- comic_widget.desktop | 5 + comic_widget.py | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++ images/down.png | Bin 0 -> 1332 bytes images/down1.png | Bin 0 -> 1408 bytes images/left.png | Bin 0 -> 1409 bytes images/left1.png | Bin 0 -> 1456 bytes images/right.png | Bin 0 -> 1408 bytes images/right1.png | Bin 0 -> 1472 bytes images/star.png | Bin 0 -> 1513 bytes images/star1.png | Bin 0 -> 1584 bytes images/up.png | Bin 0 -> 1356 bytes images/up1.png | Bin 0 -> 1422 bytes 12 files changed, 429 insertions(+) create mode 100644 comic_widget.desktop create mode 100644 comic_widget.py create mode 100644 images/down.png create mode 100644 images/down1.png create mode 100644 images/left.png create mode 100644 images/left1.png create mode 100644 images/right.png create mode 100644 images/right1.png create mode 100644 images/star.png create mode 100644 images/star1.png create mode 100644 images/up.png create mode 100644 images/up1.png delete mode 100644 welcome diff --git a/comic_widget.desktop b/comic_widget.desktop new file mode 100644 index 0000000..fc41d4b --- /dev/null +++ b/comic_widget.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=Comic Widget +Comment=Simple Comic widget +Type=python +X-Path=comic_widget.py diff --git a/comic_widget.py b/comic_widget.py new file mode 100644 index 0000000..a253b72 --- /dev/null +++ b/comic_widget.py @@ -0,0 +1,424 @@ +import hildon +import hildondesktop +import gtk +import csv +import urllib2 +import urllib +import string +import os +import osso + +# constants. dbfile is the location of the csv +# comiccache is the location of the images +dbfile = "/home/user/.comicdb.csv" +comiccache = "/home/user/MyDocs/comics/" +comics = {"xkcd":{"name":"xkcd","link":"http://xkcd.org/","start":666,"dbfile":"/home/user/.comicdb.xkcd.csv"}, + "sinfest":{"name":"Sinfest","link":"http://sinfest.com/","start":3400,"dbfile":"/home/user/.comicdb.sinfest.csv"}} + +# handling of the comics +class ComicDb(): + def __init__(self, comic): + #if no db, start from this comic id. + self.comic = comic + self.start = comics[self.comic]["start"] + self.dbfile = comics[self.comic]["dbfile"] + + #if db file exist, read it + if os.path.isfile(self.dbfile) == True: + dbf = open(self.dbfile, 'r') + # if not, create it. + else: + dbf = open(self.dbfile, 'w') + dbf.write('comic,id,link,url,filename,title\n') + dbf.close() + dbf = open(self.dbfile, 'r') + self.fetch_latest_std(self.comic, self.start) + + if not os.path.exists(comiccache + self.comic + "/"): + os.makedirs(comiccache + self.comic + "/") + dbr = csv.DictReader(dbf) + self.db = [] + for row in dbr: + self.db.insert(0,row) + dbf.close() + self.currentcomic = 0 + + def get_comic(self): + print str(self.currentcomic) + if self.currentcomic < 0: + self.refresh() + self.currentcomic = 0 + if len(self.db) < (self.currentcomic + 1): + self.currentcomic -= 1 + self.refresh() + self.currentcomic = len(self.db) - 1 + + dbrow = self.db[self.currentcomic] + filename = comiccache + self.comic + "/" + dbrow['filename'] + print filename + "\n" + + if os.path.isfile(filename): + print "found, returning " + filename + "\n" + return filename + else: + #fetch file + print filename + " not found\nretrieving " + dbrow['url'] + "\n" + urllib.urlretrieve(dbrow['url'], filename) + print "success\n" + return filename + + def get_current_row(self): + return self.db[self.currentcomic] + + def get_link(self): + print str(self.currentcomic) + if self.currentcomic < 0: + self.refresh() + self.currentcomic = 0 + if len(self.db) < (self.currentcomic + 1): + self.currentcomic -= 1 + return self.db[self.currentcomic]['link'] + + + + def insert_row(self, irow): + dbf = open(self.dbfile, 'a') + dbw = csv.writer(dbf) + dbw.writerow(irow) + dbf.close() + + def insert_row_first(self, irow): + dbf = open(self.dbfile, 'w') + dbf.write('comic,id,link,url,filename,title\n') + dbw = csv.writer(dbf) + dbw.writerow(irow) + dbf.close() + dbf = open(self.dbfile, 'a') + dbw = csv.DictWriter(dbf, ['comic','id','link','url','filename','title']) + tmpdb = self.db + tmpdb.reverse() + for row in tmpdb: + dbw.writerow(row) + dbf.close() + + def refresh(self): + if len(self.db) < 1: + self.fetch_latest_std(self.comic, self.start) + elif self.currentcomic == 0 or self.currentcomic < 0: + self.fetch_latest_std(self.comic, self.db[0]['id']) + elif self.currentcomic == (len(self.db) - 1): + self.fetch_earlier(self.comic, self.db[self.currentcomic]['id']) + dbf = open(self.dbfile, 'r') + dbr = csv.DictReader(dbf) + self.db = [] + for row in dbr: + self.db.insert(0,row) + dbf.close() + + def fetch_earlier(self, comic, earliest): + number = int(earliest) - 1 + if number < 0: + return False + print "number is now: " + str(number) + "\n" + if comic == 'xkcd': + link = "http://xkcd.org/" + str(number) + "/" + elif comic == 'sinfest': + link = "http://www.sinfest.net/archive_page.php?comicID=" + str(number) + else: + return False + print "link: " + link + "\n" + try: + f = urllib2.urlopen(link) + hcode = f.code + except: + hcode = 404 + print "got hcode = " + str(hcode) + "\n" + if (hcode != 200): + return False + else: + # Build db entry. + # they look like: comic,id,link,url,filename,title + # We already have comic, number/id and link + + if comic == 'xkcd': + s = f.read() + f.close() + splt = string.split(s, "

Image URL (for hotlinking/embedding): ", 1) + splt2 = string.split(splt[1], "

", 1) + url = splt2[0] + splt = string.split(splt[0], "

", 1) + splt = string.split(splt[1], "

", 1) + title = splt[0] + + elif comic == 'sinfest': + s = f.read() + f.close() + splt = string.split(s, 'height="107"', 1) + splt = string.split(splt[1], '', 1)
+				url = splt[0]
+				if len(url) < 49:
+					print ', 1) + title = splt[0] + splt2 = string.rsplit(url, "/", 1) + filename = splt2[1] + irow = [comic,number,link,url,filename,title] + print "got irow: " + print irow + print "\ninserting first...\n" + self.insert_row_first(irow) + + + def fetch_latest_std(self, comic, latest): + + print "fetching new after " + str(comic) + " " + str(latest) + "\n" + hcode = 200 + number = int(latest) + 1 + while hcode == 200: + print "number is now: " + str(number) + "\n" + if comic == 'xkcd': + link = "http://xkcd.org/" + str(number) + "/" + elif comic == 'sinfest': + link = "http://www.sinfest.net/archive_page.php?comicID=" + str(number) + else: + break + print "link: " + link + "\n" + try: + f = urllib2.urlopen(link) + hcode = f.code + except: + hcode = 404 + print "got hcode = " + str(hcode) + "\n" + if (hcode != 200): + break + else: + # Build db entry. + # they look like: comic,id,link,url,filename,title + # We already have comic, number/id and link + + if comic == 'xkcd': + s = f.read() + f.close() + splt = string.split(s, "

Image URL (for hotlinking/embedding): ", 1) + splt2 = string.split(splt[1], "

", 1) + url = splt2[0] + splt = string.split(splt[0], "

", 1) + splt = string.split(splt[1], "

", 1) + title = splt[0] + + elif comic == 'sinfest': + s = f.read() + f.close() + splt = string.split(s, 'height="107"', 1) + splt = string.split(splt[1], '', 1)
+					url = splt[0]
+					if len(url) < 49:
+						print ', 1) + title = splt[0] + + splt2 = string.rsplit(url, "/", 1) + filename = splt2[1] + irow = [comic,number,link,url,filename,title] + print "got irow: " + print irow + print "\ninserting...\n" + + self.insert_row(irow) + + number += 1 + +class ComicHomePlugin(hildondesktop.HomePluginItem): + def __init__(self): + hildondesktop.HomePluginItem.__init__(self) + self.osso_c = osso.Context("comic_widget", "0.0.1", False) + self.osso_rpc = osso.Rpc(self.osso_c) + self.keys = comics.keys() + self.keypointer = 1 + self.db = ComicDb(self.keys[self.keypointer]) + self.comicname = comics[self.keys[self.keypointer]]['name'] + screen = self.get_screen() + colormap = screen.get_rgba_colormap() + self.set_colormap(colormap) + #setup internal area + self.set_size_request(480, 230) + self.imgvpos = 0; + self.vbox = gtk.VBox() + self.hbox = gtk.HBox() + self.label = gtk.Label(self.comicname + " " + str(self.db.db[self.db.currentcomic]['id'])) + self.e_goweb = gtk.EventBox() + self.e_open = gtk.EventBox() + self.e_switch = gtk.EventBox() + self.e_next = gtk.EventBox() + self.e_prev = gtk.EventBox() + self.e_up = gtk.EventBox() + self.e_down = gtk.EventBox() + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.comic_image.show() + self.next = gtk.image_new_from_file("/home/user/comic_widget/right.png") + self.prev = gtk.image_new_from_file("/home/user/comic_widget/left.png") + self.up = gtk.image_new_from_file("/home/user/comic_widget/up.png") + self.down = gtk.image_new_from_file("/home/user/comic_widget/down.png") + self.switch = gtk.image_new_from_file("/home/user/comic_widget/star.png") + + self.e_goweb.add(self.label) + self.e_goweb.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_goweb.connect("button-press-event", self.view_comic) + + self.e_open.add(self.comic_image) + + self.e_switch.add(self.switch) + self.e_switch.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_switch.connect("button-press-event", self.switch_comic) + + + self.e_next.add(self.next) + self.e_next.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_next.connect("button-press-event", self.get_next) + + self.e_prev.add(self.prev) + self.e_prev.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_prev.connect("button_press_event", self.get_prev) + + self.e_up.add(self.up) + self.e_up.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_up.connect("button-press-event", self.scroll_up) + + self.e_down.add(self.down) + self.e_down.set_events(gtk.gdk.BUTTON_PRESS_MASK) + self.e_down.connect("button-press-event", self.scroll_down) + + self.vbox.pack_start(self.e_open,False,False,0) + self.vbox.pack_end(self.hbox,False,False,0) + + self.hbox.pack_start(self.e_switch,False,False,5) + self.hbox.pack_start(self.e_goweb,False,False,5) + self.hbox.pack_end(self.e_next,False,False,5) + self.hbox.pack_end(self.e_down,False,False,5) + self.hbox.pack_end(self.e_up,False,False,5) + self.hbox.pack_end(self.e_prev,False,False,5) + self.vbox.show_all() + self.add(self.vbox) + + + def switch_comic(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + self.keypointer = (self.keypointer + 1) % 2 + self.imgvpos = 0 + self.db = [] + print "switching to " + self.keys[self.keypointer] + self.comicname = comics[self.keys[self.keypointer]]['name'] + self.db = ComicDb(self.keys[self.keypointer]) + self.e_goweb.remove(self.label) + self.label = gtk.Label(self.comicname + " " + str(self.db.db[self.db.currentcomic]['id'])) + self.e_goweb.add(self.label) + self.e_goweb.show_all() + self.e_open.remove(self.comic_image) + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.e_open.add(self.comic_image) + self.e_open.show_all() + + def switch_rel(self, widget, event): + if not event.type == gtk.gdk.BUTTON_RELEASE: + return False + widget.remove(self.switch) + self.switch = gtk.image_new_from_file("/home/user/comic_widget/star.png") + widget.add(self.switch) + widget.show_all() + + def view_comic(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + link = self.db.get_link() + self.osso_rpc.rpc_run_with_defaults("osso_browser", "open_new_window", (link,)) + + def scroll_down(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + self.e_open.remove(self.comic_image) + self.imgvpos = self.imgvpos + 50 + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.e_open.add(self.comic_image) + self.e_open.show_all() + + def scroll_up(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + self.e_open.remove(self.comic_image) + self.imgvpos = self.imgvpos - 50 + if self.imgvpos < 0: + self.imgvpos = 0 + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.e_open.add(self.comic_image) + self.e_open.show_all() + + def get_next(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + self.db.currentcomic -= 1 + self.imgvpos = 0 + self.e_open.remove(self.comic_image) + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.e_open.add(self.comic_image) + self.e_open.show_all() + self.e_goweb.remove(self.label) + self.label = gtk.Label(self.comicname + " " + str(self.db.db[self.db.currentcomic]['id'])) + self.e_goweb.add(self.label) + self.e_goweb.show_all() + + def get_prev(self, widget, event): + if not event.type == gtk.gdk.BUTTON_PRESS: + return False + self.db.currentcomic += 1 + self.imgvpos = 0 + self.e_open.remove(self.comic_image) + self.comic_image = self.get_resized_pixmap(self.db.get_comic(), self.imgvpos) + self.e_open.add(self.comic_image) + self.e_open.show_all() + self.e_goweb.remove(self.label) + self.label = gtk.Label(self.comicname + " " + str(self.db.db[self.db.currentcomic]['id'])) + self.e_goweb.add(self.label) + self.e_goweb.show_all() + + def get_resized_pixmap(self, filename, vpos): + pixbuf = gtk.gdk.pixbuf_new_from_file(filename) + parent_buf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pixbuf.get_has_alpha(), 8, 478, 190) + parent_buf.fill(0x00000000) + retimg = gtk.Image() + # get correct width/height + new_width = 0 + new_height = 1 + new_width = 478 + new_height = int((478.0 / pixbuf.get_width()) * pixbuf.get_height()) + scaled_buf = pixbuf.scale_simple(new_width,new_height,gtk.gdk.INTERP_BILINEAR) + + if scaled_buf.get_height() > 190: + if scaled_buf.get_height() - (vpos + 190) < 0: + vpos = scaled_buf.get_height() - 190 + scaled_buf.copy_area(0,vpos,478,190,parent_buf,0,0) + + else: + tmpy = 190 - scaled_buf.get_height() + if tmpy > 0: + tmpy = tmpy / 2 + + scaled_buf.copy_area(0,vpos,478,scaled_buf.get_height(),parent_buf,0,tmpy) + + retimg.set_from_pixbuf(parent_buf) + return retimg + + +hd_plugin_type = ComicHomePlugin + + +if __name__ == "__main__": + import gobject + gobject.type_register(hd_plugin_type) + obj = gobject.new(hd_plugin_type, plugin_id="plugin_id") + obj.show_all() + gtk.main() diff --git a/images/down.png b/images/down.png new file mode 100644 index 0000000000000000000000000000000000000000..650aa28d1de58fa00e12cfc6c20afc0c6d26e6b0 GIT binary patch literal 1332 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 KMNUMnLSTX{v8WUP literal 0 HcmV?d00001 diff --git a/images/right.png b/images/right.png new file mode 100644 index 0000000000000000000000000000000000000000..1f2764fa7ecea846c54c91265390edc79cdee276 GIT binary patch literal 1408 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 P00000NkvXXu0mjfTeH6| literal 0 HcmV?d00001 diff --git a/images/star1.png b/images/star1.png new file mode 100644 index 0000000000000000000000000000000000000000..cfaaad71b54243f691775e13eb2a730989328d66 GIT binary patch literal 1584 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 literal 0 HcmV?d00001 diff --git a/welcome b/welcome deleted file mode 100644 index e69de29..0000000 -- 1.7.9.5