Just another quick addition...does this interface for adding tags make
sense?  I kinda like it, but I can see it being confusing at first....

On 9/17/06, Edward Duffy <[EMAIL PROTECTED]> wrote:
Here's an update where I've started some work on the bottom pane.
It's still going to need some serious love, but some of the idea's in
Jamie's Sep 14 email are starting to be incorporated.

Oh..and I got rid of setting the layout and max results by environment
variable...not sure why I did that...any way, now they are command
line arguments, and their form is "--layout=2" and "--max-hits=50".

On 9/15/06, Jamie McCracken <[EMAIL PROTECTED]> wrote:
> Edward Duffy wrote:
> > I got tired of writing C, so I wrote some prototype GUIs in Python.
> > So here's 4, all rolled into 1 script.  To switch between the four,
> > you need to set the TRACKER_UI_LAYOUT environment variable, so
> >
> > TRACKER_UI_LAYOUT=1 ./tracker-gui-prototypes.py
> > Uses a GtkComboBox to select services; results in IconView
> >
> > TRACKER_UI_LAYOUT=2 ./tracker-gui-prototypes.py
> > Uses a GtkTreeView + ListStore to select services; results in IconView
> >
> > TRACKER_UI_LAYOUT=3 ./tracker-gui-prototypes.py
> > Uses a GtkNotebook to select services; results in IconView (broken...I
> > can't create the tabs until I know which services are relavant to the
> > results)
> >
> > TRACKER_UI_LAYOUT=4 ./tracker-gui-prototypes.py
> > Just finds everything and puts it in a GtkTreeStore
> >
> > You can also change the number of results returned at once by setting
> > the TRACKER_MAX_RESULTS environment variable,
> >
> > TRACKER_UI_LAYOUT=1 TRACKER_MAX_RESULTS=50 ./tracker-gui-prototypes.py
> >
> > Would show fifty at a time..default is 20.
> >
> >
>
> Thanks - They all look good in their own way!
>
>
> --
> Mr Jamie McCracken
> http://jamiemcc.livejournal.com/
>



#!/usr/bin/env python

import os
import os.path
import sys

import gtk
import gconf
import dbus

import md5
import urllib

from getopt import getopt

SERVICES = [
      ( 'Files',             'system-file-manager'      ),
      ( 'Folders',           'folder'                   ),
      ( 'Applications',      'application-x-executable' ),
      ( 'Conversations',     'face-smile-big'           ),
      ( 'Contacts',          'x-office-address-book'    ),
      ( 'Development Files', 'applications-development' ),
      ( 'Documents',         'x-office-document'        ),
      ( 'Emails',            'email'                    ),
      ( 'Images',            'image-x-generic'          ),
      ( 'Music',             'audio-x-generic'          ),
      ( 'Text Files',        'text-x-generic'           ),
      ( 'Videos',            'video-x-generic'          ),
   ]

DISPLAY_METADATA = {
      'Files'     : [ 'file.name', 'file.path', 'file.size', 'file.rank',
                      'file.permissions', 'file.format' ],
      'Music'     : [ 'audio.artist', 'audio.title' ],
      'Documents' : [ 'doc.title', 'doc.author', 'doc.subject' ],
      'Text Files': [ 'file.content' ],
      'Development Files': [ 'file.content' ],
      'Images'    : [ 'image.height', 'image.width' ],
      'Videos'    : [ ],
   }

METADATA_WIDTH = 4

def FileSizeFormat(hbox, size):
   label = gtk.Label()
   size = int(size)
   if size < 2**10:
      label.set_text('%d bytes' % size)
   elif size < 2**20:
      label.set_text('%3.1f KB' % (size / 2.**10))
   elif size < 2**30:
      label.set_text('%3.1f MB' % (size / 2.**20))
   elif size < 2**40:
      label.set_text('%3.1f GB' % (size / 2.**30))
   hbox.pack_start(label, expand=False, fill=False)

def FileRankFormat(hbox, rank):
   if not rank:
      rank='7'
   i = 0
   rank = float(rank) / 2.
   while rank > 0.5:
      hbox.pack_start(gtk.image_new_from_icon_name('weather-clear', gtk.ICON_SIZE_MENU))
      i += 1
      rank -= 1
   if rank == 0.5:
      hbox.pack_start(gtk.image_new_from_icon_name('weather-few-clouds', gtk.ICON_SIZE_MENU))
      i += 1
   while i < 5:
      hbox.pack_start(gtk.image_new_from_icon_name('weather-overcast', gtk.ICON_SIZE_MENU))
      i += 1

METADATA_FORMATTERS = {
      'file.size':FileSizeFormat,
      'file.rank':FileRankFormat,
}

class Tracker:
   def __init__(self):
      bus = dbus.SessionBus()
      obj = bus.get_object('org.freedesktop.Tracker',
                           '/org/freedesktop/tracker')
      self.tracker = dbus.Interface(obj, 'org.freedesktop.Tracker')
      self.files = dbus.Interface(obj, 'org.freedesktop.Tracker.Files')
      self.search = dbus.Interface(obj, 'org.freedesktop.Tracker.Search')
      self.keywords = dbus.Interface(obj, 'org.freedesktop.Tracker.Keywords')
      self.metadata = dbus.Interface(obj, 'org.freedesktop.Tracker.Metadata')

   def __getattr__(self, attr):
      return getattr(self.tracker, attr)

class TrackerGUI:

   def __init__(self):
      
      self.gconf = gconf.client_get_default()
      self.icon_theme = gtk.icon_theme_get_default()
      self.tracker = Tracker()

      self.layout = 1
      self.max_results = 20
      opts,args = getopt(sys.argv[1:], '', ['layout=', 'max-hits='])
      for a,v in opts:
         if a == '--layout':
            self.layout = int(v)
         elif a == '--max-hits':
            self.max_results = int(v)

      self.window = gtk.Window()
      self.window.set_title('Tracker')

      main_vbox = gtk.VBox()
      #main_vbox.set_border_width(12)
      main_vbox.set_spacing(6)

      hbox = gtk.HBox()
      hbox.set_spacing(6)
      hbox.set_border_width(12)

      label = gtk.Label('_Search:')
      hbox.pack_start(label, expand=False, fill=False)

      self.search_entry = gtk.Entry()
      hbox.pack_start(self.search_entry, expand=True, fill=True)

      label.props.use_underline = True
      label.set_mnemonic_widget(self.search_entry)

      if self.layout == 1:
         self.service_combobox = gtk.ComboBox()
         store = gtk.ListStore(gtk.gdk.Pixbuf, str)
         self.service_combobox.set_model(store)
         cell = gtk.CellRendererPixbuf()
         self.service_combobox.pack_start(cell, False)
         self.service_combobox.add_attribute(cell, 'pixbuf', 0)
         cell = gtk.CellRendererText()
         self.service_combobox.pack_start(cell, False)
         self.service_combobox.add_attribute(cell, 'text', 1)
         for label, icon_name in SERVICES:
            pixbuf = self.icon_theme.load_icon(icon_name,
               gtk.ICON_SIZE_MENU, gtk.ICON_LOOKUP_USE_BUILTIN)
            store.append([pixbuf, label])
         self.service_combobox.set_active(0)
         hbox.pack_start(self.service_combobox, expand=False, fill=True)


      self.search_button = gtk.Button(stock='gtk-find')
      self.search_button.set_data('offset', 0)
      hbox.pack_start(self.search_button, expand=False, fill=False)

      main_vbox.pack_start(hbox, expand=False, fill=False)

      hbox = gtk.HBox()

      if self.layout == 2:
         self.service_treeview = gtk.TreeView()
         store = gtk.ListStore(gtk.gdk.Pixbuf, str)
         self.service_treeview.set_model(store)
         #self.service_treeview.set_sensitive(False)
         self.service_treeview.set_headers_visible(False)
         self.service_treeview.set_row_separator_func(lambda model,iter: model[iter][1] == '')

         cell = gtk.CellRendererPixbuf()
         col = gtk.TreeViewColumn('', cell, pixbuf=0)
         self.service_treeview.append_column(col)
         cell = gtk.CellRendererText()
         col = gtk.TreeViewColumn('', cell, text=1)
         self.service_treeview.append_column(col)

         for label, icon_name in SERVICES:
            pixbuf = self.icon_theme.load_icon(icon_name,
               gtk.ICON_SIZE_MENU, gtk.ICON_LOOKUP_USE_BUILTIN)
            store.append([pixbuf, label])
         store.append([None,''])
         tags = self.tracker.keywords.GetList('Files')
         for tag,count in tags.items():
            store.append([None, tag])
      
         selection = self.service_treeview.get_selection()
         selection.select_iter(store.get_iter_first())
         selection.connect('changed', self.on_service_selection_changed)

         sw = gtk.ScrolledWindow()
         sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
         sw.set_shadow_type(gtk.SHADOW_IN)
         sw.add(self.service_treeview)
         hbox.pack_start(sw, expand=False, fill=True)
         
      sw = gtk.ScrolledWindow()
      sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
      sw.set_shadow_type(gtk.SHADOW_IN)

      if self.layout in (1,2,3,):
         self.icon_view = gtk.IconView()
         store = gtk.ListStore(gtk.gdk.Pixbuf, str, str)
         self.icon_view.set_model(store)
         self.icon_view.set_pixbuf_column(0)
         self.icon_view.set_text_column(1)
         sw.add(self.icon_view)
      elif self.layout == 4:
         self.results_tree = gtk.TreeView()
         store = gtk.TreeStore(gtk.gdk.Pixbuf, str, str)
         self.results_tree.set_model(store)
         self.results_tree.set_headers_visible(False)

         cell = gtk.CellRendererPixbuf()
         col = gtk.TreeViewColumn('', cell, pixbuf=0)
         self.results_tree.append_column(col)
         cell = gtk.CellRendererText()
         col = gtk.TreeViewColumn('', cell, text=1)
         self.results_tree.append_column(col)

         self._fill_in_services(store)
         sw.add(self.results_tree)
         sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)

      if self.layout == 3:
         self.notebook = gtk.Notebook()
         label = gtk.Label('Files')
         self.notebook.append_page(sw, label)
         main_vbox.pack_start(self.notebook, expand=True, fill=True)
      else:
         hbox.pack_start(sw, expand=True, fill=True)
         main_vbox.pack_start(hbox, expand=True, fill=True)

      if self.layout != 4:
         hbox = gtk.HBox()
         hbox.set_spacing(6)

         self.last_button = gtk.Button(stock='gtk-goto-last')
         self.last_button.set_sensitive(False)
         hbox.pack_end(self.last_button, expand=False, fill=False)

         self.next_button = gtk.Button(stock='gtk-go-forward')
         self.next_button.get_children()[0].get_children()[0].get_children()[1].set_text('Next')
         self.next_button.set_sensitive(False)
         hbox.pack_end(self.next_button, expand=False, fill=False)

         image = gtk.image_new_from_stock('gtk-go-back', gtk.ICON_SIZE_BUTTON)
         self.previous_button = gtk.Button()
         self.previous_button.add(image)
         self.previous_button.set_sensitive(False)
         hbox.pack_end(self.previous_button, expand=False, fill=False)
      
         image = gtk.image_new_from_stock('gtk-goto-first', gtk.ICON_SIZE_BUTTON)
         self.first_button = gtk.Button()
         self.first_button.add(image)
         self.first_button.set_sensitive(False)
         self.first_button.set_data('offset', 0)
         hbox.pack_end(self.first_button, expand=False, fill=False)
      
         main_vbox.pack_start(hbox, expand=False, fill=False)

      self.bottom_pane = gtk.HBox()
      self.bottom_pane_content = gtk.HBox()
      self.bottom_pane.pack_start(self.bottom_pane_content, expand=True, fill=True)

      self.hide_bottom_pane_button = gtk.Button()
      image = gtk.image_new_from_stock('gtk-close', gtk.ICON_SIZE_MENU)
      self.hide_bottom_pane_button.add(image)
      self.hide_bottom_pane_button.set_relief(gtk.RELIEF_NONE)
      vbox = gtk.VBox()
      vbox.pack_start(self.hide_bottom_pane_button, expand=False, fill=False)
      self.bottom_pane.pack_start(vbox, expand=False, fill=False)

      vbox = gtk.VBox()
      vbox.pack_start(main_vbox, expand=True, fill=True)
      vbox.pack_start(self.bottom_pane, expand=False, fill=True)
      self.statusbar = gtk.Statusbar()
      vbox.pack_start(self.statusbar, expand=False, fill=False)
      
      self.window.add(vbox)

      self.search_entry.connect('activate', lambda e: self.search_button.emit('clicked'))
      self.search_button.connect('clicked', self.get_search_stats)
      if self.layout == 3:
         self.search_button.connect('clicked', self.build_service_tabs)

      if self.layout == 4:
         self.search_button.connect('clicked', self.on_complete_search)
         self.results_tree.get_selection().connect('changed', self.on_result_selected)
      else:
         self.icon_view.connect('selection-changed', self.on_result_selected)
         self.search_button.connect('clicked', self.on_search)
         self.first_button.connect('clicked', self.on_search)
         self.next_button.connect('clicked', self.on_search)
         self.previous_button.connect('clicked', self.on_search)
         self.last_button.connect('clicked', self.on_search)
      self.hide_bottom_pane_button.connect('clicked', lambda *e: self.bottom_pane.hide())
      self.window.connect('destroy', gtk.main_quit)

      self.window.resize(640, 480)
      self.window.show_all()

   def get_search_stats(self, button):
      return
      self.search_stats = { }
      done = False
      offset = 0
      query = self.search_entry.get_text()
      while not done:
         results = self.tracker.search.Text(-1, 'Files', query, offset, 100, False)
         print len(results)
         for r in results:
            service = self.tracker.files.GetServiceType(r)
            print r,service
            self.search_stats.setdefault(service, 0)
            self.search_stats[service] += 1
         offset += len(results)
         done = (len(results) < 100)
      print self.search_stats

   def on_search(self, button):
      query = self.search_entry.get_text()
      offset = button.get_data('offset')
      if self.layout == 1:
         service = SERVICES[self.service_combobox.get_active()][0]
      elif self.layout == 2:
         store, iter = self.service_treeview.get_selection().get_selected()
         service = store[iter][1]
      elif self.layout == 3:
         service = self.notebook.get_tab_label(self.icon_view.get_parent()).get_text()

      if query.startswith('tag:'):
         results = self.tracker.keywords.Search(-1, service,
                              [ query[4:] ], offset, self.max_results + 1)
      else:
         results = self.tracker.search.Text(-1, service,
                              query, offset, self.max_results + 1, False)

      if self.layout in (1,2,3,):
         store = self.icon_view.get_model()
         store.clear()
         for r in results[:self.max_results]:
            basename = os.path.basename(r)
            pixbuf = self._get_file_thumbnail_pixbuf(r) or self._get_mime_thumbnail(r)
            store.append([ pixbuf, basename, r ])
         self.icon_view.get_parent().get_vscrollbar().get_adjustment().value = 0

      self.first_button.set_sensitive(True)
      if offset > 0:
         self.previous_button.set_sensitive(True)
         self.previous_button.set_data('offset', offset - self.max_results)
      else:
         self.previous_button.set_sensitive(False)

      if len(results) == self.max_results + 1:
         self.next_button.set_sensitive(True)
         self.next_button.set_data('offset', offset + self.max_results)
      else:
         self.next_button.set_sensitive(False)

   def on_complete_search(self, button):
      query = self.search_entry.get_text()
      store = self.results_tree.get_model()
      store.clear()
      self._fill_in_services(store)
      iter = store.get_iter_first()
      while iter:
         service = store[iter][1]
         done = False
         offset = 0
         while not done:
            if query.startswith('tag:'):
               results = self.tracker.keywords.Search(-1, service,
                              [ query[4:] ], offset, self.max_results + 1)
            else:
               results = self.tracker.search.Text(-1, service, query, offset, 256, False)
            offset += len(results)
            for r in results:
               pixbuf = self._get_file_thumbnail_pixbuf(r) or self._get_mime_thumbnail(r)
               h = pixbuf.get_height()
               w = pixbuf.get_width()
               scale = max(h,w)
               h = int(h * 24. / scale)
               w = int(w * 24. / scale)
               pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
               store.append(iter, [pixbuf, r, r])
            done = (len(results) < 256)
         iter = store.iter_next(iter)

   def on_service_selection_changed(self, selection):
      self.on_search(self.search_button)
      
   def _get_file_thumbnail_pixbuf(self, f):
      uri = 'file://%s' % urllib.pathname2url(f)
      thumb = md5.new()
      thumb.update(uri)
      thumb = os.path.expanduser('~/.thumbnails/normal/%s.png' % thumb.hexdigest())
      try:
         return gtk.gdk.pixbuf_new_from_file(thumb)
      except:
         return None

   def _get_mime_thumbnail(self, f):
      return self.icon_theme.load_icon('image-loading', 32, gtk.ICON_LOOKUP_USE_BUILTIN)

   def _fill_in_services(self, store):
      for label, icon_name in SERVICES:
         pixbuf = self.icon_theme.load_icon(icon_name,
            gtk.ICON_SIZE_MENU, gtk.ICON_LOOKUP_USE_BUILTIN)
         store.append(None, [pixbuf, label, ''])

   def build_service_tabs(self, button):
      query = self.search_entry.get_text()
      services = set()

      done = False
      offset = 0
      while not done:
         results = self.tracker.search.Text(-1, 'Files', query, offset, 256, False)
         print len(results),'results.',results
         for r in results:
            print r, self.tracker.files.GetServiceType(r)
            #services.add(self.tracker.files.GetServiceType(r))
         offset += len(results)
         done = (len(results) < 256)
         print 'done:',done
      print services

   def on_result_selected(self, arg):
      self._clear_container(self.bottom_pane_content)
      if isinstance(arg, gtk.IconView):
         store = arg.get_model()
         selected = arg.get_selected_items()[0]
         path = store[selected][2]
      else:
         model, iter = arg.get_selected()
         path = model[iter][2]
      if not path:
         return
      service = self.tracker.files.GetServiceType(path)
      print 'Selected file: [%s]' % path
      self.statusbar.push(0, '%s [%s]' % (path,service))
      if service in DISPLAY_METADATA:
         keys = DISPLAY_METADATA['Files'] + DISPLAY_METADATA[service]
      else:
         keys = DISPLAY_METADATA['Files']
      metadata = self.tracker.metadata.Get('Files', path, keys)
      height = len(keys) / METADATA_WIDTH + 1
      print height,'x',METADATA_WIDTH
      table = gtk.Table(height + 1, METADATA_WIDTH * 2, False)
      table.set_row_spacings(6)
      table.set_col_spacings(12)
      for n,(k,m) in enumerate(zip(keys,metadata)):
         i = n % height
         j = n / height * 2
         label = gtk.Label('')
         label.set_markup('<b>%s:</b>' % k.split('.')[1].title())
         hbox = gtk.HBox()
         hbox.pack_end(label, expand=False, fill=False)
         table.attach(hbox, j, j+1, i, i+1, gtk.FILL, gtk.FILL, False, False)
         hbox = gtk.HBox()
         if k in METADATA_FORMATTERS:
            METADATA_FORMATTERS[k](hbox, m)
         else:
            label = gtk.Label(m)
            label.set_selectable(True)
            hbox.pack_start(label, fill=False, expand=False)
         table.attach(hbox, j+1, j+2, i, i+1, gtk.FILL, gtk.FILL, False, False)
      label = gtk.Label('')
      label.set_markup('<b>Tags:</b>')
      hbox = gtk.HBox()
      hbox.pack_end(label, expand=False, fill=False)
      table.attach(hbox, 0, 1, height, height+1, gtk.FILL, gtk.FILL, False, False)
      hbox = gtk.HBox()
      tags = self.tracker.keywords.Get('Files', path)
      first = True
      for tag in tags:
         if not first:
            hbox.pack_start(gtk.Label(','), expand=False, fill=False)
         print tag
         btn = gtk.Button()
         label = gtk.Label()
         label.set_markup('<span foreground="blue"><u>%s</u></span>' % tag)
         btn.add(label)
         btn.set_relief(gtk.RELIEF_NONE)
         btn.connect('clicked', self.on_tag_selected)
         hbox.pack_start(btn, expand=False, fill=False)
         first = False

      tag_entry = gtk.Entry()
      hbox.pack_start(tag_entry, expand=False, fill=False)

      add_button = gtk.Button()
      image = gtk.image_new_from_stock('gtk-add', gtk.ICON_SIZE_BUTTON)
      add_button.add(image)
      add_button.set_relief(gtk.RELIEF_NONE)
      hbox.pack_start(add_button, expand=False, fill=False)
      
      accept_button = gtk.Button()
      image = gtk.image_new_from_stock('gtk-apply', gtk.ICON_SIZE_BUTTON)
      accept_button.add(image)
      accept_button.set_relief(gtk.RELIEF_NONE)
      hbox.pack_start(accept_button, expand=False, fill=False)
      
      cancel_button = gtk.Button()
      image = gtk.image_new_from_stock('gtk-cancel', gtk.ICON_SIZE_BUTTON)
      cancel_button.add(image)
      cancel_button.set_relief(gtk.RELIEF_NONE)
      hbox.pack_start(cancel_button, expand=False, fill=False)
      
      table.attach(hbox, 1, METADATA_WIDTH*2, height, height+1, gtk.FILL, gtk.FILL, False, False)
      self.bottom_pane_content.pack_start(table, fill=True, expand=True)
      table.show_all()

      tag_entry.connect('activate', lambda e: accept_button.emit('clicked'))
      add_button.connect('clicked', self.on_add_tag, tag_entry, accept_button, cancel_button)
      accept_button.connect('clicked', self.on_accept_tag_add, add_button, tag_entry, cancel_button, path, arg)
      cancel_button.connect('clicked', self.on_cancel_tag_add, add_button, tag_entry, accept_button)

      tag_entry.hide()
      accept_button.hide()
      cancel_button.hide()

   def _clear_container(self, widget, parent=None):
      if isinstance(widget, gtk.Container):
         widget.foreach(self._clear_container, widget)
      if parent:
         parent.remove(widget)
   
   def on_tag_selected(self, button):
      tag = button.get_children()[0].get_text()
      self.search_entry.set_text('tag:%s' % tag)
      self.search_button.emit('clicked')

   def on_add_tag(self, add_button, tag_entry, accept_button, cancel_button):
      add_button.hide()
      tag_entry.show()
      accept_button.show()
      cancel_button.show()
      tag_entry.grab_focus()

   def on_accept_tag_add(self, accept_button, add_button, tag_entry, cancel_button, path, selection):
      self.tracker.keywords.Add('Files', path, [tag_entry.get_text()])
      self.on_result_selected(selection)

   def on_cancel_tag_add(self, cancel_button, add_button, tag_entry, accept_button):
      tag_entry.set_text('')
      tag_entry.hide()
      accept_button.hide()
      cancel_button.hide()
      add_button.show()


gui = TrackerGUI()
gtk.main()
_______________________________________________
tracker-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/tracker-list

Reply via email to