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
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()
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')
gui = TrackerGUI()
gtk.main()
_______________________________________________
tracker-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/tracker-list