Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAgustin Zubiaga <aguz@sugarlabs.org>2013-06-19 00:07:06 (GMT)
committer Agustin Zubiaga <aguz@sugarlabs.org>2013-06-19 00:07:06 (GMT)
commitf9e1665e34d2bdc5ddd7779be02a779730649a21 (patch)
treeec1d6aff16250f00bff580c65414b946431f9bfd
parentbe4ee1d11a69c75f82747b5e473992978bc5ef74 (diff)
Adding search featureHEADmaster
-rw-r--r--activity.py61
-rw-r--r--bt.py29
-rw-r--r--icons/bluetooth.sugar.svg19
-rw-r--r--icons/bluetooth.svg74
-rw-r--r--icons/paired-devices.svg82
-rw-r--r--widgets.py63
6 files changed, 291 insertions, 37 deletions
diff --git a/activity.py b/activity.py
index 1ca1070..0be3b1d 100644
--- a/activity.py
+++ b/activity.py
@@ -18,17 +18,26 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from gi.repository import Gtk
+from gi.repository import GObject
from sugar3.activity import activity
from sugar3.activity.widgets import ActivityToolbarButton
from sugar3.activity.widgets import StopButton
-from sugar3.graphics.toolbutton import ToolButton
+from sugar3.graphics.radiotoolbutton import RadioToolButton
from sugar3.graphics.toolbarbox import ToolbarBox
from widgets import DeviceList
+from widgets import EmptyWidgets
from bt import Bluetooth
-#from gettext import gettext as _
+from gettext import gettext as _
+
+import logging
+
+# Logging
+_logger = logging.getLogger('bluetooth-activity')
+_logger.setLevel(logging.DEBUG)
+logging.basicConfig()
class ActivityBluetooth(activity.Activity):
@@ -43,7 +52,7 @@ class ActivityBluetooth(activity.Activity):
# Canvas
self._notebook = Gtk.Notebook()
- #self._notebook.set_show_tabs(False)
+ self._notebook.set_show_tabs(False)
self.set_canvas(self._notebook)
scroll = Gtk.ScrolledWindow()
@@ -57,12 +66,24 @@ class ActivityBluetooth(activity.Activity):
scroll = Gtk.ScrolledWindow()
scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
- self._notebook.append_page(scroll, Gtk.Label())
self._nbdevices = DeviceList()
scroll.add(self._nbdevices)
scroll.show_all()
+ self.progressbar = Gtk.ProgressBar()
+
+ nbbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ nbbox.pack_start(scroll, True, True, 0)
+ nbbox.pack_start(self.progressbar, False, True, 0)
+
+ self._notebook.append_page(nbbox, Gtk.Label())
+
+ self._ew = EmptyWidgets()
+ self._ew.connect('search-again', self._find_devices)
+
+ self._notebook.append_page(self._ew, Gtk.Label())
+
# Toolbars
toolbarbox = ToolbarBox()
@@ -71,7 +92,16 @@ class ActivityBluetooth(activity.Activity):
toolbarbox.toolbar.insert(Gtk.SeparatorToolItem(), -1)
- search_btn = ToolButton(icon_name='system-search')
+ pdevices_btn = RadioToolButton()
+ pdevices_btn.set_tooltip(_("Paired devices"))
+ pdevices_btn.props.icon_name = 'paired-devices'
+ pdevices_btn.connect('clicked', lambda w: self._notebook.set_current_page(0))
+ toolbarbox.toolbar.insert(pdevices_btn, -1)
+
+ search_btn = RadioToolButton()
+ search_btn.set_tooltip(_("Search devices"))
+ search_btn.props.icon_name = 'system-search'
+ search_btn.props.group = pdevices_btn
search_btn.connect('clicked', self._find_devices)
toolbarbox.toolbar.insert(search_btn, -1)
@@ -82,14 +112,31 @@ class ActivityBluetooth(activity.Activity):
stopbtn = StopButton(self)
toolbarbox.toolbar.insert(stopbtn, -1)
+
+ self._notebook.set_current_page(0)
self.set_toolbar_box(toolbarbox)
self.show_all()
def _find_devices(self, widget):
self._notebook.set_current_page(1)
+ self._times = 0
+ GObject.timeout_add(100, self._update_bar)
self.bluetooth.find_devices()
- self.nbdevices.model.clear()
+ self._nbdevices.model.clear()
def _device_found(self, bluetooth, deviceprops):
- self.nbdevices.add_device(deviceprops) \ No newline at end of file
+ self._nbdevices.add_device(deviceprops)
+
+ def _update_bar(self):
+ self._times += 0.1
+ self.progressbar.set_fraction(self._times / 20.0)
+
+ if self._times >= 20.0:
+ self.bluetooth.stop_search()
+ if not self.bluetooth.nearby_devices:
+ self._notebook.set_current_page(2)
+ return False
+ else:
+ return True
+
diff --git a/bt.py b/bt.py
index 72241ea..8686780 100644
--- a/bt.py
+++ b/bt.py
@@ -27,16 +27,28 @@ SEARCH_TIME = 20 # seconds
class Bluetooth(GObject.GObject):
__gsignals__ = {
- 'device-found': (GObject.SignalFlags.RUN_FIRST, None, [object])}
+ 'device-found': (GObject.SignalFlags.RUN_FIRST, None, [object]),
+ 'search-finished': (GObject.SignalFlags.RUN_FIRST, None, [])}
def __init__(self):
super(Bluetooth, self).__init__()
manager = bluez.Manager('gobject')
self._adapter = manager.DefaultAdapter()
- #self._adapter.HandleSignal(self._device_found, 'DeviceFound')
- self._nearby_devices = []
+ def device_found(address, properties):
+ already_found = False
+ for i in self.nearby_devices:
+ if properties['Address'] == i['Address']:
+ already_found = True
+
+ if not already_found:
+ self.nearby_devices.append(properties)
+ self.emit('device-found', properties)
+
+ self._adapter.HandleSignal(device_found, 'DeviceFound')
+
+ self.nearby_devices = []
self._paired_devices = self._adapter.ListDevices()
def get_paired_devices(self):
@@ -44,13 +56,8 @@ class Bluetooth(GObject.GObject):
def find_devices(self):
self._adapter.StartDiscovery()
- self._nearby_devices = []
- GObject.timeout_add(SEARCH_TIME * 1000, self._stop_search)
+ self.nearby_devices = []
- def _device_found(self, address, properties):
- self._nearby_devices.append(properties)
- self.emit('device-found', properties)
- return
-
- def _stop_search(self):
+ def stop_search(self):
self._adapter.StopDiscovery()
+
diff --git a/icons/bluetooth.sugar.svg b/icons/bluetooth.sugar.svg
new file mode 100644
index 0000000..793d3da
--- /dev/null
+++ b/icons/bluetooth.sugar.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#ffffff">
+]><svg height="100%" id="svg2" inkscape:version="0.48.4 r9939" sodipodi:docname="bluetooth.svg" version="1.1" viewBox="25 36 700 978" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <metadata id="metadata14">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs12"/>
+ <sodipodi:namedview bordercolor="#000000" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview10" inkscape:current-layer="g4" inkscape:cx="647.75513" inkscape:cy="371.0704" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="809" inkscape:window-x="0" inkscape:window-y="27" inkscape:zoom="0.1866066" objecttolerance="10" pagecolor="#ffffff" showgrid="false"/>
+ <g fill="&fill_color;" id="g4" style="fill:#000000">
+ <rect height="976" id="rect6" ry="291" style="fill:" width="640" x="51" y="37"/>
+ <path d="M208,367 513,674 366,852V216L513,386 208,685" id="path8" stroke="&stroke_color;" stroke-width="53" style="stroke:#ffffff"/>
+ </g>
+</svg> \ No newline at end of file
diff --git a/icons/bluetooth.svg b/icons/bluetooth.svg
index 2f18167..4a68659 100644
--- a/icons/bluetooth.svg
+++ b/icons/bluetooth.svg
@@ -1,19 +1,69 @@
-<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
- <!ENTITY stroke_color "#000000">
- <!ENTITY fill_color "#ffffff">
-]><svg height="100%" id="svg2" inkscape:version="0.48.4 r9939" sodipodi:docname="Bluetooth.svg" version="1.1" viewBox="25 36 700 978" width="100%" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
- <metadata id="metadata14">
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="100%"
+ id="svg2"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="bluetooth.svg"
+ version="1.1"
+ viewBox="25 36 700 978"
+ width="100%">
+ <metadata
+ id="metadata14">
<rdf:RDF>
- <cc:Work rdf:about="">
+ <cc:Work
+ rdf:about="">
<dc:format>image/svg+xml</dc:format>
- <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
- <defs id="defs12"/>
- <sodipodi:namedview bordercolor="#000000" borderopacity="1" gridtolerance="10" guidetolerance="10" id="namedview10" inkscape:current-layer="g4" inkscape:cx="1127.0127" inkscape:cy="371.07041" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="809" inkscape:window-x="0" inkscape:window-y="27" inkscape:zoom="0.24130879" objecttolerance="10" pagecolor="#ffffff" showgrid="false"/>
- <g fill="&fill_color;" id="g4" style="fill:#000000">
- <rect height="976" id="rect6" ry="291" style="fill:&fill_color;" width="640" x="51" y="37"/>
- <path d="M208,367 513,674 366,852V216L513,386 208,685" id="path8" stroke="&stroke_color;" stroke-width="53" style="fill:&fill_color;"/>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ bordercolor="#000000"
+ borderopacity="1"
+ gridtolerance="10"
+ guidetolerance="10"
+ id="namedview10"
+ inkscape:current-layer="g4"
+ inkscape:cx="647.75513"
+ inkscape:cy="371.0704"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="480"
+ inkscape:window-maximized="0"
+ inkscape:window-width="809"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:zoom="0.1866066"
+ objecttolerance="10"
+ pagecolor="#ffffff"
+ showgrid="false" />
+ <g
+ fill="#ffffff"
+ id="g4"
+ style="fill:#000000">
+ <rect
+ height="976"
+ id="rect6"
+ ry="291"
+ style="fill:"
+ width="640"
+ x="51"
+ y="37" />
+ <path
+ d="M208,367 513,674 366,852V216L513,386 208,685"
+ id="path8"
+ stroke="#000000"
+ stroke-width="53"
+ style="stroke:#ffffff" />
</g>
</svg>
diff --git a/icons/paired-devices.svg b/icons/paired-devices.svg
new file mode 100644
index 0000000..634f1d2
--- /dev/null
+++ b/icons/paired-devices.svg
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="100%"
+ id="svg2"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="paired-devices.svg"
+ version="1.1"
+ viewBox="25 36 46 40"
+ width="100%">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ bordercolor="#000000"
+ borderopacity="1"
+ gridtolerance="10"
+ guidetolerance="10"
+ id="namedview10"
+ inkscape:current-layer="g4"
+ inkscape:cx="-10.014973"
+ inkscape:cy="20.642716"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="541"
+ inkscape:window-maximized="1"
+ inkscape:window-width="1024"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:zoom="2.82709"
+ objecttolerance="10"
+ pagecolor="#ffffff"
+ showgrid="false" />
+ <g
+ id="g4"
+ style="fill:#000000"
+ transform="translate(0,-938)">
+ <rect
+ style="fill:#ffffff;fill-opacity:1"
+ height="35.087963"
+ id="rect6-2"
+ ry="10.461678"
+ width="21.720196"
+ x="-70.708466"
+ y="975.94318"
+ transform="scale(-1,1)" />
+ <path
+ d="m 65.380239,987.80712 -10.351032,11.03693 4.988859,6.39915 v -22.86465 l -4.988859,6.11163 10.351032,10.74927"
+ id="path8-1"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.85127914;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1"
+ height="35.087963"
+ id="rect6-2-3"
+ ry="10.461678"
+ width="21.720196"
+ x="24.505846"
+ y="976.04321" />
+ <path
+ d="m 29.834074,987.90714 10.351031,11.03691 -4.988858,6.39925 v -22.86478 l 4.988858,6.11162 -10.351031,10.7493"
+ id="path8-1-2"
+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.85127914;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/widgets.py b/widgets.py
index 821b60c..1f3cff9 100644
--- a/widgets.py
+++ b/widgets.py
@@ -20,10 +20,14 @@
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkPixbuf
+from gi.repository import GObject
import os
from sugar3.activity import activity
from sugar3.graphics import style
+from sugar3.graphics.icon import Icon
+
+from gettext import gettext as _
ICONS_DIR = os.path.join(activity.get_bundle_path(), "icons")
@@ -44,7 +48,7 @@ class DeviceList(Gtk.TreeView):
self.model = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str)
self.set_model(self.model)
- column = Gtk.TreeViewColumn()
+ column = Gtk.TreeViewColumn('Devices')
cell = Gtk.CellRendererPixbuf()
column.pack_start(cell, False)
column.add_attribute(cell, "pixbuf", 0)
@@ -52,14 +56,14 @@ class DeviceList(Gtk.TreeView):
column = Gtk.TreeViewColumn()
cell = Gtk.CellRendererText()
- column.pack_start(cell, False)
- column.add_attribute(cell, "text", 1)
+ column.pack_start(cell, True)
+ column.add_attribute(cell, "markup", 1)
self.append_column(column)
column = Gtk.TreeViewColumn()
cell = Gtk.CellRendererText()
column.pack_start(cell, False)
- column.add_attribute(cell, "text", 2)
+ column.add_attribute(cell, "markup", 2)
self.append_column(column)
#self.modify_base(Gdk.Color(255, 255, 255))
@@ -67,9 +71,54 @@ class DeviceList(Gtk.TreeView):
def add_device(self, i):
icon = get_icon(i["Icon"])
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon,
- style.MEDIUM_ICON_SIZE, style.MEDIUM_ICON_SIZE)
- self.model.append([pixbuf, i["Name"], i["Address"]])
+ style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
+ self.model.append([pixbuf, '<b>%s</b>' % i["Name"],
+ '<b>%s</b>' % i["Address"]])
def set_devices(self, devices):
for i in devices:
- self.add_device(i) \ No newline at end of file
+ self.add_device(i)
+
+
+class EmptyWidgets(Gtk.EventBox):
+
+ __gsignals__ = {
+ 'search-again': (GObject.SignalFlags.RUN_FIRST, None, [])}
+
+ def __init__(self):
+ Gtk.EventBox.__init__(self)
+
+ self.modify_bg(Gtk.StateType.NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ mvbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
+ vbox.pack_start(mvbox, True, False, 0)
+
+ image_icon = Icon(pixel_size=style.LARGE_ICON_SIZE,
+ file='activity/activity-bluetooth.svg',
+ stroke_color=style.COLOR_BUTTON_GREY.get_svg(),
+ fill_color=style.COLOR_TRANSPARENT.get_svg())
+ mvbox.pack_start(image_icon, False, False, style.DEFAULT_PADDING)
+
+ label = Gtk.Label('<span foreground="%s"><b>%s</b></span>' %
+ (style.COLOR_BUTTON_GREY.get_html(),
+ _('No devices found')))
+ label.set_use_markup(True)
+ mvbox.pack_start(label, False, False, style.DEFAULT_PADDING)
+
+ hbox = Gtk.Box()
+ search_btn = Gtk.Button()
+ search_btn.connect('clicked', lambda w: self.emit('search-again'))
+ refresh_image = Gtk.Image.new_from_stock(Gtk.STOCK_REFRESH,
+ Gtk.IconSize.BUTTON)
+ buttonbox = Gtk.Box()
+ buttonbox.pack_start(refresh_image, False, True, 0)
+ buttonbox.pack_end(Gtk.Label(_('Search again')), True, True, 5)
+ search_btn.add(buttonbox)
+ hbox.pack_start(search_btn, True, False, 0)
+ mvbox.pack_start(hbox, False, False, style.DEFAULT_PADDING)
+
+ self.add(vbox)
+ self.show_all()
+