#!/usr/bin/env python # example helloworld.py import pygtk pygtk.require('2.0') import gtk import gobject import re import os.path, os import urllib2 import sys import gzip import StringIO import thread class Updater: def __init__(self): # First we need to read all the online libraries try: fd = open(os.getenv("HOME") + "/.gEDA/online_libs") lines = fd.readlines() fd.close() except: lines = ['lib-gz http://www.zerties.org/~stettberger/gschem/symbols.gz'] fd = open(os.getenv("HOME") + "/.gEDA/online_libs", "w+") for line in lines: fd.write(line + "\n") fd.close() print "ERROR: Can't read ~/.gEDA/online_libs!\nUsing %s"%lines[0] self.archives = [] for line in lines: if line.startswith("lib"): self.archives.append(line.strip().split(" ", 1)) def catch(self, archive_nr): if len(self.archives) <= archive_nr: return False try: url = urllib2.urlopen(self.archives[archive_nr][1]) data = url.read() url.close() except: return "ERROR: Can't download: " + self.archives[archive_nr][1] if self.archives[archive_nr][0] == "lib-gz": try: return gzip.GzipFile(fileobj = StringIO.StringIO(data)).read().split("\n") except: return "ERROR: Can't decompress data" return data.split() def symbol_count(self, symbols): count = 0 # Symbol count before removing double symbols for i in symbols: if i.startswith("Filename:"): count += 1 return count def delete_doublettes(self, symbols): new_symbols = [] sym = None for line in symbols + ['Filename: dummy/data']: if line.startswith("Filename: "): if sym != None: append = True for i in range(0, len(new_symbols)): if new_symbols[i]['filename'] == sym['filename']: if new_symbols[i].has_key('version') and sym.has_key('version'): if new_symbols[i]['version'] < sym['version']: del new_symbols[i] else: append = False else: append = False if append: new_symbols.append(sym) sym = {} sym['filename'] = os.path.basename(line.split(" ", 1)[1]) sym['data'] = [] if line.startswith("Symbol-Version: "): sym['version'] = eval(line.split(" ", 1)[1].replace(".", "* 100 +")) if (sym): sym['data'].append(line) symbols = [] for new in new_symbols: for line in new['data']: symbols.append(line) return symbols def write_symbol_cache(self, symbols): try: fd = open(os.getenv("HOME") + "/.gEDA/symbols_cache", "w+") for i in symbols: fd.write(i + "\n") fd.close() except: return "Can't write symbol cache to: " + os.getenv("HOME") + "/.gEDA/symbols_cache" def update(self): i = 0 list = [] while True: symbols = self.catch(i) i += 1 if symbols == False: break if symbols.__class__.__name__ == "String": print symbols continue print "Got %d symbols"%self.symbol_count(symbols) list += symbols before = self.symbol_count(list) list = self.delete_doublettes(list) after = self.symbol_count(list) err = self.write_symbol_cache(list) if err: print err return print "%d Symbol; %d doubles deleted"%(after, before-after) def parse_file(file): fd = open(file, "r") i = 0 ret = [{}] for line in fd.readlines(): if line.startswith("Filename: "): i = i + 1 ret.append({}) ret[i]['file'] = os.path.basename(line.split(" ", 1)[1].strip("\n")) ret[i]['filename'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Image: "): ret[i]['image'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Description: "): ret[i]['desc'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Documentation: "): ret[i]['doc'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Symbol-Version: "): ret[i]['version'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Author: "): ret[i]['author'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Use-License: "): ret[i]['use-license'] = line.split(" ", 1)[1].strip("\n") elif line.startswith("Dist-License: "): ret[i]['dist-license'] = line.split(" ", 1)[1].strip("\n") return ret try: os.mkdir(os.getenv("HOME") + "/.gEDA") except: pass # Directory does already exist if not os.path.exists(os.getenv("HOME") + "/.gEDA/symbols_cache"): u = Updater() u.update() class GafOnLib: def delete_event(self, widget, event, data=None): return False def destroy(self, widget, data=None): gtk.main_quit() def search_change_callback(self, widget, data): regex = re.compile(".*" + data.get_text() + ".*", re.I) found = [] for i in self.db: if (i.has_key('file') and regex.match(i['file'])) or (i.has_key('desc') and regex.match(i['desc'])): found.append(i) self.filtered = found self.treeview.set_model(self.create_model()) def row_activate(self, view, detail_treeview = None): selection = view.get_selection() model, iter = selection.get_selected() symbol = self.filtered[model.get_path(iter)[0]] self.treeview.scroll_to_cell(model.get_path(iter)) # This is a hack :( gobject.idle_add(self.treeview.scroll_to_cell, model.get_path(iter)) # create list store model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_BOOLEAN ) for key, desc in [["filename", "Filename:"], \ ["desc", "Description:"], \ ["doc", "Documentation:"], \ ["version", "Version:"], \ ["author", "Author:"], \ ["use-license", "Use-License:"], \ ["dist-license","Dist-License:"]]: iter = model.append() data = "" if symbol.has_key(key): data = symbol[key] model.set (iter, 0, desc, 1,data, 2, True) detail_treeview.set_model(model) self.detail_box.show() self.last_thread = thread.start_new_thread(self.update_detail_image, tuple([symbol])) def update_detail_image(self, symbol): if (self.detail_image.get_pixbuf()): width = self.detail_image.get_pixbuf().get_width() height = self.detail_image.get_pixbuf().get_height() self.detail_image.clear() self.detail_image.set_size_request(width, height) try: data = urllib2.urlopen(symbol['image']).read() except: print "Can't load image for: " + symbol['filename'] return else: pixbuf_loader = gtk.gdk.PixbufLoader() pixbuf_loader.write(data, len(data)) pixbuf_loader.close() pixbuf = pixbuf_loader.get_pixbuf() pixbuf = pixbuf.scale_simple(pixbuf.get_width()/2, \ pixbuf.get_height()/2, gtk.gdk.INTERP_BILINEAR) self.detail_image.set_from_pixbuf(pixbuf) width = self.detail_image.get_pixbuf().get_width() height = self.detail_image.get_pixbuf().get_height() self.detail_image.set_size_request(width, height) self.detail_image.show() def create_model(self): # create list store model = gtk.ListStore( gobject.TYPE_STRING, gobject.TYPE_STRING ) # add items for filter in self.filtered: iter = model.append() desc = "" if filter.has_key("desc"): desc = filter['desc'] model.set (iter, 0, filter['file'], 1, desc) return model def download_symbol(self,widget,view): selection = view.get_selection() model, iter = selection.get_selected() symbol = self.filtered[model.get_path(iter)[0]] chooser = gtk.FileChooserDialog(title="Save symbol to...",action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK)) chooser.set_current_name(symbol['file']) chooser.set_current_folder(os.getenv("HOME") + "/.gEDA") response = chooser.run() if response != gtk.RESPONSE_OK: chooser.destroy() return filename = chooser.get_filename() chooser.destroy() try: data = urllib2.urlopen(symbol['filename']).read() fd = open(filename, "w+") fd.write(data) fd.close() except: dialog = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Couldn't download symbol:\n" + symbol['filename']) def update_symbol_cache(self, widget, data): self.update_label.show() update = Updater() i = 0 list = [] while True: text = self.update_label.get_text() symbols = update.catch(i) i += 1 if symbols == False: break if symbols.__class__.__name__ == "String": self.update_label.set_text(text + "\n" + symbols) continue self.update_label.set_text(text + "\nGot %d symbols"%update.symbol_count(symbols)) list += symbols gtk.main_iteration(block=False) before = update.symbol_count(list) list = update.delete_doublettes(list) after = update.symbol_count(list) err = update.write_symbol_cache(list) if err: text = self.update_label.get_text() self.update_label.set_text(text + "\n" + err) return text = self.update_label.get_text() self.update_label.set_text(text + "\n%d Symbol; %d doubles deleted"%(after, before-after)) gtk.main_iteration(block=False) gobject.timeout_add(5000, lambda : self.update_label.hide() and False) text = self.update_label.get_text() self.update_label.set_text(text + "\nNote: This window will close automatically") self.db = parse_file(os.getenv("HOME") + "/.gEDA/symbols_cache") self.filtered = [] def __init__(self): self.db = parse_file(os.getenv("HOME") + "/.gEDA/symbols_cache") self.filtered = [] # create a new window self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) # When the window is given the "delete_event" signal (this is given # by the window manager, usually by the "close" option, or on the # titlebar), we ask it to call the delete_event () function # as defined above. The data passed to the callback # function is NULL and is ignored in the callback function. self.window.connect("delete_event", self.delete_event) # Here we connect the "destroy" event to a signal handler. # This event occurs when we call gtk_widget_destroy() on the window, # or if we return FALSE in the "delete_event" callback. self.window.connect("destroy", self.destroy) # Sets the border width of the window. self.window.set_border_width(10) # Creates a new button with the label "Hello World". self.search = gtk.Entry(max = 0) self.search.connect("activate", self.search_change_callback, self.search) self.button = gtk.Button("Search") self.button.connect("clicked", self.search_change_callback, self.search) # Filter list sw = gtk.ScrolledWindow() sw.set_shadow_type(gtk.SHADOW_ETCHED_IN) sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # Treeview with colums self.treeview = gtk.TreeView(self.create_model()) self.treeview.set_rules_hint(True) self.treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) ## ## Add Colums ## model = self.treeview.get_model() # column renderer = gtk.CellRendererText() renderer.set_data("column", 0) column = gtk.TreeViewColumn("File", renderer, text=0) self.treeview.append_column(column) # description column renderer = gtk.CellRendererText() renderer.set_data("column", 1) column = gtk.TreeViewColumn("Description", renderer, text=1) self.treeview.append_column(column) sw.add(self.treeview) # Detail Treeview with colums self.detail_treeview = gtk.TreeView(self.create_model()) self.detail_treeview.set_rules_hint(True) self.detail_treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) ## ## Add Colums ## # column renderer = gtk.CellRendererText() renderer.set_data("column", 0) column = gtk.TreeViewColumn("Key", renderer, text=0) self.detail_treeview.append_column(column) # description column renderer = gtk.CellRendererText() renderer.set_data("column", 1) column = gtk.TreeViewColumn("Value", renderer, text=1, editable=2) self.detail_treeview.append_column(column) # Buttons action_hbox = gtk.HBox(homogeneous=False, spacing=0) button = gtk.Button("Update Symbol Cache") button.connect("clicked", self.update_symbol_cache, None) action_hbox.pack_start(button) button = gtk.Button("Download") button.connect("clicked", self.download_symbol, self.treeview) action_hbox.pack_start(button) # Detail buffer self.treeview.connect("cursor-changed", self.row_activate, self.detail_treeview) self.treeview.connect("row-activated", lambda *w:self.download_symbol(None, self.treeview)) self.detail_image = gtk.Image() self.detail_image.set_from_pixbuf(None) self.update_label = gtk.Label() # Start packing self.search_box = gtk.HBox(homogeneous=False, spacing=0) self.search_box.pack_start(self.search, expand=True, fill=True, padding=0) self.search_box.pack_start(self.button, expand=False, fill=False, padding=0) self.detail_box = gtk.HBox(homogeneous = False, spacing = 5) self.detail_box.pack_start(self.detail_treeview, expand = True) self.detail_box.pack_start(self.detail_image, expand = True) vpaned = gtk.VPaned() vpaned.set_border_width(5) vpaned.pack1(sw,resize=True,shrink = False) vpaned.pack2(self.detail_box,resize=False, shrink = False) self.vbox = gtk.VBox(homogeneous = False, spacing = 0) self.vbox.pack_start(self.search_box, expand = False) self.vbox.pack_start(vpaned) self.vbox.pack_start(action_hbox, expand = False) self.vbox.pack_start(self.update_label, expand = True) # This packs the button into the window (a GTK container). self.window.add(self.vbox) # The final step is to display this newly created widget. # and the window self.window.show_all() self.search_change_callback(None, self.search) self.detail_box.hide() self.update_label.hide() def main(self): gobject.threads_init() # All PyGTK applications must have a gtk.main(). Control ends here # and waits for an event to occur (like a key press or mouse event). gtk.main() # If the program is run directly or passed as an argument to the python # interpreter then create a HelloWorld instance and show it if __name__ == "__main__": gafon = GafOnLib() gafon.main()