From 4cdd7605aa4da3f30e289d2f389ffdfbb8c3f95a Mon Sep 17 00:00:00 2001 From: flavio Date: Wed, 17 Oct 2012 00:31:28 +0000 Subject: Gtk3 Port --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4842d8c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.py[co] +*.bak diff --git a/activity/activity.info b/activity/activity.info index d31aba4..c22f693 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -1,6 +1,6 @@ [Activity] name = Backup -activity_version = 5 +activity_version = 6 bundle_id = org.sugarlabs.Backup exec = sugar-activity backup.BackupActivity icon = Backup_icon diff --git a/backup.py b/backup.py index 3a56910..9ccf946 100644 --- a/backup.py +++ b/backup.py @@ -29,26 +29,21 @@ import traceback import zipfile import dbus -import gobject -import gtk - -#from sugar.activity.widgets import ActivityToolbarButton -try: - from sugar.activity.widgets import StopButton - from sugar.graphics.icon import CellRendererIcon - from sugar.graphics.toolbarbox import ToolbarBox - pre_086_toolbars = False - -except ImportError: - from sugar.graphics.toolbox import Toolbox - pre_086_toolbars = True - -from sugar.activity import activity -import sugar.env -from sugar.graphics import style -from sugar.graphics.toolbutton import ToolButton -import sugar.logger -from sugar import profile +import gi +from gi.repository import GObject +from gi.repository import Gtk +from gi.repository import Gio + +from sugar3.activity.widgets import StopButton +from sugar3.graphics.icon import CellRendererIcon +from sugar3.graphics.toolbarbox import ToolbarBox + +from sugar3.activity import activity +import sugar3.env +from sugar3.graphics import style +from sugar3.graphics.toolbutton import ToolButton +import sugar3.logger +from sugar3 import profile try: import json @@ -96,20 +91,7 @@ class BackupButton(ToolButton): self.props.accelerator = 'b' -if pre_086_toolbars: - class StopButton(ToolButton): - - def __init__(self, activity, **kwargs): - ToolButton.__init__(self, 'activity-stop', **kwargs) - self.props.tooltip = _('Stop') - self.props.accelerator = 'Q' - self.connect('clicked', self.__stop_button_clicked_cb, activity) - - def __stop_button_clicked_cb(self, button, activity): - activity.close() - - -class AsyncBackup(gobject.GObject): +class AsyncBackup(GObject.GObject): """ Run a data store backup asynchronously. """ @@ -117,14 +99,14 @@ class AsyncBackup(gobject.GObject): _METADATA_JSON_NAME = '_metadata.json' __gsignals__ = { - 'progress': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + 'progress': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ([int, int])), - 'done': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([str])), + 'done': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ([])), + 'error': (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ([str])), } def __init__(self, mount_point): - gobject.GObject.__init__(self) + GObject.GObject.__init__(self) self._mount_point = mount_point self._path = None self._bundle = None @@ -174,9 +156,9 @@ class AsyncBackup(gobject.GObject): os.close(to_child_read_fd) self._pipe_from_child = os.fdopen(from_child_read_fd, 'r') self._pipe_to_child = os.fdopen(to_child_write_fd, 'w') - self._pipe_from_child_watch_id = gobject.io_add_watch( + self._pipe_from_child_watch_id = GObject.io_add_watch( self._pipe_from_child, - gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP, + GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP, self._child_io_cb) def abort(self): @@ -186,7 +168,7 @@ class AsyncBackup(gobject.GObject): def _child_io_cb(self, source_, condition): """Receive and handle message from child.""" - if condition in [gobject.IO_ERR, gobject.IO_HUP]: + if condition in [GObject.IO_ERR, GObject.IO_HUP]: logging.debug('error condition: %r', condition) self.emit('error', _('Lost connection to child process').encode('utf-8')) @@ -240,7 +222,7 @@ class AsyncBackup(gobject.GObject): def _parent_close(self): """Close connections to child and wait for it.""" - gobject.source_remove(self._pipe_from_child_watch_id) + GObject.source_remove(self._pipe_from_child_watch_id) self._pipe_from_child.close() self._pipe_to_child.close() pid_, status = os.waitpid(self._child_pid, 0) @@ -456,7 +438,9 @@ class BackupActivity(activity.Activity): _MEDIA_COMBO_FREE_COLUMN = 3 def __init__(self, handle): + activity.Activity.__init__(self, handle, create_jobject=False) + self.max_participants = 1 self._progress_bar = None self._message_box = None @@ -464,9 +448,13 @@ class BackupActivity(activity.Activity): self._media_combo = None self._backup_button = None self._backup = None - self._hal_devices = {} self._num_entries = None self._color = profile.get_color() + + self.volume_monitor = Gio.VolumeMonitor.get() + self.volume_monitor.connect('mount-added', self._gio_mount_added_cb) + self.volume_monitor.connect('mount-removed', self._gio_mount_removed_cb) + self._setup_widgets() self._find_media() @@ -490,80 +478,62 @@ class BackupActivity(activity.Activity): def label_size_allocate(widget, rect): widget.set_size_request(rect.width, -1) - vbox = gtk.VBox() + vbox = Gtk.VBox() instruction = _('Please plug in the storage device you want to back' ' up to, select it in the tool bar and press the' ' button to start the backup.') - instruction_label = gtk.Label(instruction.encode('utf-8')) + instruction_label = Gtk.Label(instruction.encode('utf-8')) instruction_label.set_line_wrap(True) instruction_label.connect('size-allocate', label_size_allocate) - vbox.pack_start(instruction_label, True) + vbox.pack_start(instruction_label, True, True, 0) self.set_canvas(vbox) vbox.show_all() def _setup_toolbar(self): - if pre_086_toolbars: - self.toolbox = Toolbox() - self.set_toolbox(self.toolbox) - - toolbar = gtk.Toolbar() - self.toolbox.add_toolbar('Toolbar', toolbar) - toolbar_box = self.toolbox - - else: - toolbar_box = ToolbarBox() - toolbar = toolbar_box.toolbar - self.set_toolbar_box(toolbar_box) - - self._media_combo_model = gtk.ListStore(str, str, str, str) - self._media_combo = gtk.ComboBox(self._media_combo_model) - if not pre_086_toolbars: - icon_renderer = CellRendererIcon(self._media_combo) - icon_renderer.props.xo_color = self._color - icon_renderer.props.width = style.STANDARD_ICON_SIZE + \ - style.DEFAULT_SPACING - icon_renderer.props.height = style.STANDARD_ICON_SIZE - icon_renderer.props.size = style.STANDARD_ICON_SIZE - icon_renderer.props.xpad = style.DEFAULT_PADDING - icon_renderer.props.ypad = style.DEFAULT_PADDING - self._media_combo.pack_start(icon_renderer, False) - self._media_combo.add_attribute(icon_renderer, 'icon_name', - self._MEDIA_COMBO_ICON_COLUMN) - # FIXME: icon_renderer for pre-0.86 - - name_renderer = gtk.CellRendererText() + + toolbar_box = ToolbarBox() + toolbar = toolbar_box.toolbar + self.set_toolbar_box(toolbar_box) + + self._media_combo_model = Gtk.ListStore(str, str, str, str) + self._media_combo = Gtk.ComboBox() + self._media_combo.set_model(self._media_combo_model) + + icon_renderer = CellRendererIcon(self._media_combo) + icon_renderer.props.xo_color = self._color + icon_renderer.props.width = style.STANDARD_ICON_SIZE + \ + style.DEFAULT_SPACING + icon_renderer.props.height = style.STANDARD_ICON_SIZE + icon_renderer.props.size = style.STANDARD_ICON_SIZE + icon_renderer.props.xpad = style.DEFAULT_PADDING + icon_renderer.props.ypad = style.DEFAULT_PADDING + self._media_combo.pack_start(icon_renderer, False) + self._media_combo.add_attribute(icon_renderer, 'icon_name', + self._MEDIA_COMBO_ICON_COLUMN) + + name_renderer = Gtk.CellRendererText() self._media_combo.pack_start(name_renderer, False) self._media_combo.add_attribute(name_renderer, 'text', self._MEDIA_COMBO_NAME_COLUMN) - free_renderer = gtk.CellRendererText() + free_renderer = Gtk.CellRendererText() self._media_combo.pack_start(free_renderer, False) self._media_combo.add_attribute(free_renderer, 'text', self._MEDIA_COMBO_FREE_COLUMN) - tool_item = gtk.ToolItem() + tool_item = Gtk.ToolItem() tool_item.add(self._media_combo) # FIXME: looks like plain GTK, not like Sugar tooltip_text = _('Storage medium to store the backup on') tool_item.set_tooltip_text(tooltip_text.encode('utf-8')) toolbar.insert(tool_item, -1) - - if pre_086_toolbars: - self._backup_button = gtk.ToolButton() - self._backup_button.props.icon_name = 'journal-export' - label_text = _('Backup Journal') - try: - self._backup_button.props.tooltip = label_text.encode('utf-8') - except AttributeError: - pass - - else: - self._backup_button = BackupButton() - + + self._backup_button = BackupButton() + self._backup_button.connect('clicked', self._backup_cb) self._backup_button.set_sensitive(False) toolbar.insert(self._backup_button, -1) - - separator = gtk.SeparatorToolItem() + + separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_expand(True) toolbar.insert(separator, -1) @@ -595,28 +565,24 @@ class BackupActivity(activity.Activity): def _setup_backup_view(self, mount_point): """Set up UI for showing feedback from worker process.""" - vbox = gtk.VBox(False) + vbox = Gtk.Box(orientation = Gtk.Orientation.VERTICAL) label_text = _('Backing up Journal to %s') % (mount_point, ) - label = gtk.Label(label_text.encode('utf-8')) + label = Gtk.Label(label_text.encode('utf-8')) label.set_line_wrap(True) label.connect('size-allocate', label_size_allocate) label.show() - vbox.pack_start(label) - - alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5) - alignment.show() - - self._progress_bar = gtk.ProgressBar() + vbox.pack_start(label, False, False, 0) + + self._progress_bar = Gtk.ProgressBar() self._progress_bar.props.text = _('Scanning Journal').encode('utf-8') self._progress_bar.show() - alignment.add(self._progress_bar) - vbox.add(alignment) + vbox.pack_start(self._progress_bar, False, False, 0) - self._message_box = gtk.Label() + self._message_box = Gtk.Label() label.set_line_wrap(True) label.connect('size-allocate', label_size_allocate) - vbox.pack_start(self._message_box) + vbox.pack_start(self._message_box, False, False, 0) # FIXME # self._close_button = gtk.Button(_('Abort')) @@ -642,13 +608,13 @@ class BackupActivity(activity.Activity): logging.debug('_done_cb') self._backup_button.set_sensitive(True) - vbox = gtk.VBox(False) + vbox = Gtk.VBox() label_text = _('Successfully backed up %d Journal entries.') \ % (self._num_entries, ) - label = gtk.Label(label_text.encode('utf-8')) + label = Gtk.Label(label_text.encode('utf-8')) label.set_line_wrap(True) label.connect('size-allocate', label_size_allocate) - vbox.pack_start(label) + vbox.pack_start(label, True, True, 0) self.set_canvas(vbox) self.show_all() @@ -669,20 +635,10 @@ class BackupActivity(activity.Activity): # self.emit('close') def _find_media(self): - """Fill the combo box with available storage media. - - Also sets up a callback to keep the list current. - """ - try: - import gio - except ImportError: - return self._find_media_hal() - - volume_monitor = gio.volume_monitor_get() - volume_monitor.connect('mount-added', self._gio_mount_added_cb) - volume_monitor.connect('mount-removed', self._gio_mount_removed_cb) - for mount in volume_monitor.get_mounts(): - self._gio_mount_added_cb(volume_monitor, mount) + """Fill the combo box with available storage media.""" + + for mount in self.volume_monitor.get_mounts(): + self._gio_mount_added_cb(self.volume_monitor, mount) def _gio_mount_added_cb(self, volume_monitor, mount): """Handle notification from GIO that a medium was mounted.""" @@ -690,118 +646,17 @@ class BackupActivity(activity.Activity): path = mount.get_root().get_path() name = mount.get_name() self._add_medium(path, name, icon_name) - + def _gio_mount_removed_cb(self, volume_monitor, mount): """Handle notification from GIO that a medium was unmounted.""" path = mount.get_root().get_path() self._remove_medium(path) - - def _find_media_hal(self): - """Use HAL to fill in the available storage media.""" - bus = dbus.SystemBus() - proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH) - hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE) - hal_manager.connect_to_signal('DeviceAdded', - self._hal_device_added_cb) - - for udi in hal_manager.FindDeviceByCapability('volume'): - self._hal_device_added_cb(udi) - - def _hal_device_added_cb(self, udi): - """Handle notification from GIO that a device was added.""" - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - # A just-added device might lack one of the attributes we're - # looking for, so we need to listen for changes on ALL devices. - device.connect_to_signal('PropertyModified', - lambda *args: self._hal_property_modified_cb(udi, *args)) - - try: - if device.GetProperty('volume.fsusage') != 'filesystem': - logging.debug('Ignoring non-filesystem UDI %s', udi) - return - except dbus.DBusException: - logging.debug('Ignoring UDI %s without volume.fsusage', udi) - return - - self._hal_try_device(device, udi) - - def _hal_try_device(self, device, udi): - """Possibly add device to UI.""" - if not device.GetProperty('volume.is_mounted'): - return - - path = device.GetProperty('volume.mount_point') - if path == '/': - return - - name = device.GetProperty('volume.label') - if not name: - name = device.GetProperty('volume.uuid') - - self._hal_devices[udi] = path - bus = dbus.SystemBus() - bus.add_signal_receiver(self._hal_device_removed_cb, - 'DeviceRemoved', - HAL_MANAGER_IFACE, HAL_SERVICE_NAME, - HAL_MANAGER_PATH, arg0=udi) - - self._add_medium(path, name, - self._choose_icon_name(self._hal_get_icons_for_volume(device))) - - def _hal_device_removed_cb(self, udi): - """Handle notification from GIO that a device was removed.""" - path = self._hal_devices.pop(udi, None) - if not path: - return - - self._remove_medium(path) - - def _hal_property_modified_cb(self, udi, count, changes): - """Handle notification from HAL that a property has changed.""" - if 'volume.is_mounted' in [change[0] for change in changes]: - logging.debug('volume.is_mounted changed for UDI %s', udi) - - bus = dbus.SystemBus() - device_object = bus.get_object(HAL_SERVICE_NAME, udi) - device = dbus.Interface(device_object, HAL_DEVICE_IFACE) - - # We can get multiple notifications, so need to figure out - # the current state and ignore non-changes. - if device.GetProperty('volume.is_mounted'): - if udi not in self._hal_devices: - self._hal_try_device(device, udi) - else: - logging.debug('Ignoring duplicate notification') - else: - if udi in self._hal_devices: - self._hal_device_removed_cb(udi) - else: - logging.debug('Ignoring duplicate notification') - else: - logging.debug('Ignoring change for UDI %s', udi) - - def _hal_get_icons_for_volume(self, device): - bus = dbus.SystemBus() - storage_udi = device.GetProperty('block.storage_device') - obj = bus.get_object(HAL_SERVICE_NAME, storage_udi) - storage_device = dbus.Interface(obj, HAL_DEVICE_IFACE) - - storage_drive_type = storage_device.GetProperty('storage.drive_type') - bus_type = storage_device.GetProperty('storage.bus') - if storage_drive_type == 'sd_mmc': - return ['media-flash-sd', 'media-flash-sd-mmc', 'media'] - elif bus_type == 'usb': - return ['media-flash-usb', 'media', 'media-disk'] - else: - return ['media', 'media-disk', 'media-flash-usb'] - + def _add_medium(self, path, medium_name, icon_name): """Make storage medium selectable in the UI.""" # FIXME: update space information periodically or at least after # backup run + try: stat = os.statvfs(path) except EnvironmentError: @@ -809,9 +664,9 @@ class BackupActivity(activity.Activity): return free_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] -# total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] + total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS] self._media_combo_model.append([icon_name, medium_name, path, - _('%s Free') % (format_size(free_space), )]) + _('%s Free') % (format_size(free_space), )]) self._backup_button.set_sensitive(True) if self._media_combo.get_active() == -1: self._media_combo.set_active(0) @@ -838,15 +693,14 @@ class BackupActivity(activity.Activity): def _choose_icon_name(self, names): """Choose the first valid icon name or fall back to a default name.""" - theme = gtk.icon_theme_get_default() + theme = Gtk.IconTheme.get_default() for name in names: - if theme.lookup_icon(name, gtk.ICON_SIZE_LARGE_TOOLBAR, 0): + if theme.lookup_icon(name, style.GRID_CELL_SIZE, 0): return name - return 'drive' # pylint isn't smart enough for the gettext.install() magic _ = lambda msg: msg gettext.install('backup', 'po', unicode=True) -sugar.logger.start() +sugar3.logger.start() diff --git a/setup.py b/setup.py index 28be213..0d6da87 100755 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from sugar.activity import bundlebuilder +from sugar3.activity import bundlebuilder bundlebuilder.start() -- cgit v0.9.1