New draft: Gtk+ UI directory prototype
1
draft/README
Normal file
|
@ -0,0 +1 @@
|
|||
gPodder "tres" draft implementation / prototype code
|
196
draft/directory-ui/directory.py
Normal file
|
@ -0,0 +1,196 @@
|
|||
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
import tagcloud
|
||||
import json
|
||||
|
||||
w = gtk.Dialog()
|
||||
w.set_title('Discover new podcasts')
|
||||
w.set_default_size(650, 450)
|
||||
|
||||
tv = gtk.TreeView()
|
||||
tv.set_headers_visible(False)
|
||||
tv.set_size_request(160, -1)
|
||||
|
||||
class OpmlEdit(object): pass
|
||||
class Search(object): pass
|
||||
class OpmlFixed(object): pass
|
||||
class TagCloud(object): pass
|
||||
|
||||
search_providers = (
|
||||
('gpodder.net', 'search_gpodder.png', Search),
|
||||
('YouTube', 'search_youtube.png', Search),
|
||||
('SoundCloud', 'search_soundcloud.png', Search),
|
||||
('Miro Guide', 'search_miro.png', Search),
|
||||
)
|
||||
|
||||
directory_providers = (
|
||||
('Toplist', 'directory_toplist.png', OpmlFixed),
|
||||
('Examples', 'directory_example.png', OpmlFixed),
|
||||
('Tag cloud', 'directory_tags.png', TagCloud),
|
||||
)
|
||||
|
||||
SEPARATOR = (True, pango.WEIGHT_NORMAL, '', None, None)
|
||||
C_SEPARATOR, C_WEIGHT, C_TEXT, C_ICON, C_PROVIDER = range(5)
|
||||
store = gtk.ListStore(bool, int, str, gtk.gdk.Pixbuf, object)
|
||||
|
||||
opml_pixbuf = gtk.gdk.pixbuf_new_from_file('directory_opml.png')
|
||||
store.append((False, pango.WEIGHT_NORMAL, 'OPML', opml_pixbuf, OpmlEdit))
|
||||
|
||||
store.append(SEPARATOR)
|
||||
|
||||
for name, icon, provider in search_providers:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon)
|
||||
store.append((False, pango.WEIGHT_NORMAL, name, pixbuf, provider))
|
||||
|
||||
store.append(SEPARATOR)
|
||||
|
||||
for name, icon, provider in directory_providers:
|
||||
pixbuf = gtk.gdk.pixbuf_new_from_file(icon)
|
||||
store.append((False, pango.WEIGHT_NORMAL, name, pixbuf, provider))
|
||||
|
||||
store.append(SEPARATOR)
|
||||
|
||||
for i in range(1, 5):
|
||||
store.append((False, pango.WEIGHT_NORMAL, 'Bookmark %d' % i, None, None))
|
||||
|
||||
tv.set_model(store)
|
||||
|
||||
def is_row_separator(model, iter):
|
||||
return model.get_value(iter, C_SEPARATOR)
|
||||
|
||||
tv.set_row_separator_func(is_row_separator)
|
||||
|
||||
column = gtk.TreeViewColumn('')
|
||||
cell = gtk.CellRendererPixbuf()
|
||||
column.pack_start(cell, False)
|
||||
column.add_attribute(cell, 'pixbuf', C_ICON)
|
||||
cell = gtk.CellRendererText()
|
||||
column.pack_start(cell)
|
||||
column.add_attribute(cell, 'text', C_TEXT)
|
||||
column.add_attribute(cell, 'weight', C_WEIGHT)
|
||||
tv.append_column(column)
|
||||
|
||||
def on_row_activated(treeview, path, column):
|
||||
model = treeview.get_model()
|
||||
iter = model.get_iter(path)
|
||||
|
||||
for row in model:
|
||||
row[C_WEIGHT] = pango.WEIGHT_NORMAL
|
||||
|
||||
if iter:
|
||||
model.set_value(iter, C_WEIGHT, pango.WEIGHT_BOLD)
|
||||
provider = model.get_value(iter, C_PROVIDER)
|
||||
use_provider(provider)
|
||||
|
||||
tv.connect('row-activated', on_row_activated)
|
||||
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
sw.set_shadow_type(gtk.SHADOW_IN)
|
||||
sw.add(tv)
|
||||
|
||||
sidebar = gtk.VBox()
|
||||
sidebar.set_spacing(6)
|
||||
|
||||
sidebar.pack_start(sw, True, True)
|
||||
sidebar.pack_start(gtk.Button('Add bookmark'), False, False)
|
||||
|
||||
vb = gtk.VBox()
|
||||
vb.set_spacing(6)
|
||||
|
||||
title_label = gtk.Label('Title')
|
||||
title_label.set_alignment(0, 0)
|
||||
vb.pack_start(title_label, False, False)
|
||||
|
||||
search_hbox = gtk.HBox()
|
||||
search_hbox.set_spacing(6)
|
||||
search_label = gtk.Label('')
|
||||
search_hbox.pack_start(search_label, False, False)
|
||||
search_entry = gtk.Entry()
|
||||
search_hbox.pack_start(search_entry, True, True)
|
||||
search_button = gtk.Button('')
|
||||
search_hbox.pack_start(search_button, False, False)
|
||||
|
||||
vb.pack_start(search_hbox, False, False)
|
||||
|
||||
tagcloud_sw = gtk.ScrolledWindow()
|
||||
tagcloud_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
tagcloud_sw.set_shadow_type(gtk.SHADOW_IN)
|
||||
podcast_tags = json.loads("""
|
||||
[
|
||||
{"tag": "Technology",
|
||||
"usage": 530 },
|
||||
{"tag": "Society & Culture",
|
||||
"usage": 420 },
|
||||
{"tag": "Arts",
|
||||
"usage": 400},
|
||||
{"tag": "News & Politics",
|
||||
"usage": 320}
|
||||
]
|
||||
""")
|
||||
tagcloudw = tagcloud.TagCloud(list((x['tag'], x['usage']) for x in podcast_tags), 10, 14)
|
||||
tagcloud_sw.set_size_request(-1, 130)
|
||||
tagcloud_sw.add(tagcloudw)
|
||||
vb.pack_start(tagcloud_sw, False, False)
|
||||
|
||||
podcasts_sw = gtk.ScrolledWindow()
|
||||
podcasts_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
podcasts_sw.set_shadow_type(gtk.SHADOW_IN)
|
||||
podcasts_tv = gtk.TreeView()
|
||||
podcasts_sw.add(podcasts_tv)
|
||||
vb.pack_start(podcasts_sw, True, True)
|
||||
|
||||
|
||||
hb = gtk.HBox()
|
||||
hb.set_spacing(12)
|
||||
hb.set_border_width(12)
|
||||
hb.pack_start(sidebar, False, True)
|
||||
hb.pack_start(vb, True, True)
|
||||
|
||||
w.vbox.add(hb)
|
||||
w.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
w.add_button('Subscribe', gtk.RESPONSE_OK)
|
||||
w.set_response_sensitive(gtk.RESPONSE_OK, False)
|
||||
|
||||
def use_provider(provider):
|
||||
if provider == OpmlEdit:
|
||||
search_label.set_text('URL:')
|
||||
search_button.set_label('Download')
|
||||
else:
|
||||
search_label.set_text('Search:')
|
||||
search_button.set_label('Search')
|
||||
|
||||
if provider in (OpmlEdit, Search):
|
||||
title_label.hide()
|
||||
search_hbox.show()
|
||||
search_entry.set_text('')
|
||||
def later():
|
||||
search_entry.grab_focus()
|
||||
return False
|
||||
gobject.idle_add(later)
|
||||
elif provider == TagCloud:
|
||||
title_label.hide()
|
||||
search_hbox.hide()
|
||||
else:
|
||||
if provider == OpmlFixed:
|
||||
title_label.set_text('Example stuff')
|
||||
elif provider == TagCloud:
|
||||
title_label.set_text('Tag cloud')
|
||||
title_label.show()
|
||||
search_hbox.hide()
|
||||
|
||||
tagcloud_sw.set_visible(provider == TagCloud)
|
||||
|
||||
print 'using provider:', provider
|
||||
|
||||
#w.connect('destroy', gtk.main_quit)
|
||||
w.show_all()
|
||||
|
||||
on_row_activated(tv, (0,), None)
|
||||
|
||||
w.run()
|
||||
|
||||
#gtk.main()
|
||||
|
BIN
draft/directory-ui/directory_example.png
Normal file
After Width: | Height: | Size: 916 B |
BIN
draft/directory-ui/directory_opml.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
draft/directory-ui/directory_tags.png
Normal file
After Width: | Height: | Size: 743 B |
BIN
draft/directory-ui/directory_toplist.png
Normal file
After Width: | Height: | Size: 908 B |
BIN
draft/directory-ui/search_gpodder.png
Normal file
After Width: | Height: | Size: 973 B |
BIN
draft/directory-ui/search_miro.png
Normal file
After Width: | Height: | Size: 766 B |
BIN
draft/directory-ui/search_soundcloud.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
draft/directory-ui/search_youtube.png
Normal file
After Width: | Height: | Size: 441 B |
141
draft/directory-ui/tagcloud.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
|
||||
import gtk
|
||||
import gobject
|
||||
import cgi
|
||||
|
||||
tags = (
|
||||
('Electronica', 5),
|
||||
('Reggae', 5),
|
||||
('Electro', 20),
|
||||
('Detroit Techno', 4),
|
||||
('Funk', 14),
|
||||
('Jazz', 4),
|
||||
('Minimal', 20),
|
||||
('Soulful Drum and Bass', 6),
|
||||
('Dub', 7),
|
||||
('Drum and Bass', 23),
|
||||
('Deep Techno', 7),
|
||||
('Deephouse', 27),
|
||||
('Soulful', 9),
|
||||
('Minimal Techno', 30),
|
||||
('Downtempo', 17),
|
||||
('House', 29),
|
||||
('Dubstep', 14),
|
||||
('Techno', 32),
|
||||
('Electrotech', 8),
|
||||
('Techhouse', 28),
|
||||
('Disco', 15),
|
||||
('Downbeat', 28),
|
||||
('Electrohouse', 14),
|
||||
('Hiphop', 25),
|
||||
('Trance', 6),
|
||||
('Freestyle', 14),
|
||||
('Funky House', 3),
|
||||
('Minimal House', 4),
|
||||
('Nu Jazz', 11),
|
||||
('Chill-Out', 6),
|
||||
('Breaks', 10),
|
||||
('UK Garage', 4),
|
||||
('Soul', 10),
|
||||
('Progressive House', 3),
|
||||
('Lounge', 6),
|
||||
)
|
||||
|
||||
|
||||
class TagCloud(gtk.Layout):
|
||||
__gsignals__ = {
|
||||
'selected': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
|
||||
(gobject.TYPE_STRING,))
|
||||
}
|
||||
|
||||
def __init__(self, tags, min_size=20, max_size=36):
|
||||
self.__gobject_init__()
|
||||
gtk.Layout.__init__(self)
|
||||
self._tags = tags
|
||||
self._min_size = min_size
|
||||
self._max_size = max_size
|
||||
self._min_weight = min(weight for tag, weight in self._tags)
|
||||
self._max_weight = max(weight for tag, weight in self._tags)
|
||||
self._size = 0, 0
|
||||
self._alloc_id = self.connect('size-allocate', self._on_size_allocate)
|
||||
self._init_tags()
|
||||
self._in_relayout = False
|
||||
|
||||
def _on_size_allocate(self, widget, allocation):
|
||||
self._size = (allocation.width, allocation.height)
|
||||
if not self._in_relayout:
|
||||
self.relayout()
|
||||
|
||||
def _init_tags(self):
|
||||
for tag, weight in self._tags:
|
||||
label = gtk.Label()
|
||||
markup = '<span size="%d">%s</span>' % (1000*self._scale(weight), cgi.escape(tag))
|
||||
label.set_markup(markup)
|
||||
button = gtk.ToolButton(label)
|
||||
button.connect('clicked', lambda b: self.emit('selected', tag))
|
||||
self.put(button, 1, 1)
|
||||
|
||||
def _scale(self, weight):
|
||||
weight_range = float(self._max_weight-self._min_weight)
|
||||
ratio = float(weight-self._min_weight)/weight_range
|
||||
return int(self._min_size + (self._max_size-self._min_size)*ratio)
|
||||
|
||||
def relayout(self):
|
||||
self._in_relayout = True
|
||||
x, y, max_h = 0, 0, 0
|
||||
current_row = []
|
||||
pw, ph = self._size
|
||||
def fixup_row(widgets, x, y, max_h):
|
||||
residue = (pw - x)
|
||||
x = int(residue/2)
|
||||
for widget in widgets:
|
||||
cw, ch = widget.size_request()
|
||||
self.move(widget, x, y+max(0, int((max_h-ch)/2)))
|
||||
x += cw + 10
|
||||
for child in self.get_children():
|
||||
w, h = child.size_request()
|
||||
if x + w > pw:
|
||||
fixup_row(current_row, x, y, max_h)
|
||||
y += max_h + 10
|
||||
max_h, x = 0, 0
|
||||
current_row = []
|
||||
|
||||
self.move(child, x, y)
|
||||
x += w + 10
|
||||
max_h = max(max_h, h)
|
||||
current_row.append(child)
|
||||
fixup_row(current_row, x, y, max_h)
|
||||
self.set_size(pw, y+max_h)
|
||||
def unrelayout():
|
||||
self._in_relayout = False
|
||||
return False
|
||||
gobject.idle_add(unrelayout)
|
||||
gobject.type_register(TagCloud)
|
||||
|
||||
if __name__ == '__main__':
|
||||
l = TagCloud(tags)
|
||||
|
||||
try:
|
||||
import hildon
|
||||
w = hildon.StackableWindow()
|
||||
sw = hildon.PannableArea()
|
||||
except:
|
||||
w = gtk.Window()
|
||||
w.set_default_size(600, 300)
|
||||
sw = gtk.ScrolledWindow()
|
||||
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
||||
|
||||
w.set_title('Tag cloud Demo')
|
||||
w.add(sw)
|
||||
sw.add(l)
|
||||
|
||||
def on_tag_selected(cloud, tag):
|
||||
print 'tag selected:', tag
|
||||
|
||||
l.connect('selected', on_tag_selected)
|
||||
|
||||
w.show_all()
|
||||
w.connect('destroy', gtk.main_quit)
|
||||
|
||||
gtk.main()
|
||||
|