diff options
author | Agustin Zubiaga <aguz@sugarlabs.org> | 2013-06-19 00:07:06 (GMT) |
---|---|---|
committer | Agustin Zubiaga <aguz@sugarlabs.org> | 2013-06-19 00:07:06 (GMT) |
commit | f9e1665e34d2bdc5ddd7779be02a779730649a21 (patch) | |
tree | ec1d6aff16250f00bff580c65414b946431f9bfd | |
parent | be4ee1d11a69c75f82747b5e473992978bc5ef74 (diff) |
-rw-r--r-- | activity.py | 61 | ||||
-rw-r--r-- | bt.py | 29 | ||||
-rw-r--r-- | icons/bluetooth.sugar.svg | 19 | ||||
-rw-r--r-- | icons/bluetooth.svg | 74 | ||||
-rw-r--r-- | icons/paired-devices.svg | 82 | ||||
-rw-r--r-- | widgets.py | 63 |
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 + @@ -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> @@ -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() + |