class Camera(): """Store per-camera configuration in GSettings.""" def __init__(self, camera_id, make, model): """Generate Gtk widgets and bind their properties to GSettings.""" self.photos = set() empty_camera_label.hide() builder = Builder('camera') builder.get_object('camera_label').set_text(model) # GtkScale allows the user to correct the camera's clock. offset = builder.get_object('offset') offset.connect('value-changed', self.offset_handler) offset.connect('format-value', display_offset, _('Add %dm, %ds to clock.'), _('Subtract %dm, %ds from clock.')) # These two ComboBoxTexts are used for choosing the timezone manually. # They're hidden to reduce clutter when not needed. tz_region = builder.get_object('timezone_region') tz_cities = builder.get_object('timezone_cities') for name in tz_regions: tz_region.append(name, name) tz_region.connect('changed', self.region_handler, tz_cities) tz_cities.connect('changed', self.cities_handler) # TODO we're gonna need some on screen help to explain what it even # means to select the method of determining the timezone. # Back when this was radio button in a preferences window we had more # room for verbosity, but this combobox is *so terse* that I don't # really expect anybody to understand it at all. timezone = builder.get_object('timezone_method') timezone.connect('changed', self.method_handler, tz_region, tz_cities) # Push all the widgets into the UI get_obj('cameras_view').attach_next_to( builder.get_object('camera_settings'), None, BOTTOM, 1, 1) self.offset = offset self.tz_method = timezone self.tz_region = tz_region self.tz_cities = tz_cities self.camera_id = camera_id self.make = make self.model = model self.gst = GSettings('camera', camera_id) self.gst.set_string('make', make) self.gst.set_string('model', model) self.gst.bind('offset', offset.get_adjustment(), 'value') self.gst.bind('timezone-method', timezone, 'active-id') self.gst.bind('timezone-region', tz_region, 'active') self.gst.bind('timezone-cities', tz_cities, 'active') def method_handler(self, method, region, cities): """Only show manual tz selectors when necessary.""" visible = method.get_active_id() == 'custom' region.set_visible(visible) cities.set_visible(visible) self.set_timezone() def region_handler(self, region, cities): """Populate the list of cities when a continent is selected.""" cities.remove_all() for city in get_timezone(region.get_active_id(), []): cities.append(city, city) def cities_handler(self, cities): """When a city is selected, update the chosen timezone.""" if cities.get_active_id() is not None: self.set_timezone() def set_found_timezone(self, found): """Store discovered timezone in GSettings.""" self.gst.set_string('found-timezone', found) def set_timezone(self): """Set the timezone to the chosen zone and update all photos.""" environ['TZ'] = '' case = lambda x, y=self.tz_method.get_active_id(): x == y if case('lookup'): # Note that this will gracefully fallback on system timezone # if no timezone has actually been found yet. environ['TZ'] = self.gst.get_string('found-timezone') elif case('custom'): region = self.tz_region.get_active_id() city = self.tz_cities.get_active_id() if region is not None and city is not None: environ['TZ'] = '/'.join([region, city]) tzset() self.offset_handler() def offset_handler(self, offset=None): """When the offset is changed, update the loaded photos.""" for photo in self.photos: photo.calculate_timestamp() def get_offset(self): """Return the currently selected clock offset value.""" return int(self.offset.get_value())
class Camera(GObject.GObject): """Store per-camera configuration in GSettings. >>> from common import Dummy as Photo >>> cam = Camera('unknown_camera') >>> cam.add_photo(Photo()) >>> cam.num_photos 1 >>> photo = Photo() >>> cam.add_photo(photo) >>> cam.num_photos 2 >>> cam.remove_photo(photo) >>> cam.num_photos 1 >>> Camera.generate_id({'Make': 'Nikon', ... 'Model': 'Wonder Cam', ... 'Serial': '12345'}) ('12345_nikon_wonder_cam', 'Nikon Wonder Cam') >>> Camera.generate_id({}) ('unknown_camera', 'Unknown Camera') >>> cam = Camera('canon_canon_powershot_a590_is') >>> cam.timezone_method = 'lookup' >>> environ['TZ'] 'America/Edmonton' >>> cam.timezone_method = 'offset' >>> environ['TZ'].startswith('UTC') True """ offset = GObject.property(type=int, minimum=-3600, maximum=3600) utc_offset = GObject.property(type=int, minimum=-24, maximum=24) found_timezone = GObject.property(type=str) timezone_method = GObject.property(type=str) timezone_region = GObject.property(type=str) timezone_city = GObject.property(type=str) @GObject.property(type=int) def num_photos(self): """Read-only count of the loaded photos taken by this camera.""" return len(self.photos) @staticmethod def generate_id(info): """Identifies a camera by serial number, make, and model.""" maker = info.get('Make', '').capitalize() model = info.get('Model', '') # Some makers put their name twice model = model if model.startswith(maker) else maker + ' ' + model camera_id = '_'.join(sorted(info.values())).lower().replace(' ', '_') return (camera_id.strip(' _') or 'unknown_camera', model.strip() or _('Unknown Camera')) @staticmethod def set_all_found_timezone(timezone): """"Set all cameras to the given timezone.""" for camera in Camera.instances: camera.found_timezone = timezone @staticmethod def timezone_handler_all(): """Update all of the photos from all of the cameras.""" for camera in Camera.instances: camera.timezone_handler() def __init__(self, camera_id): GObject.GObject.__init__(self) self.id = camera_id self.photos = set() # Bind properties to settings self.gst = GSettings('camera', camera_id) for prop in self.gst.list_keys(): self.gst.bind(prop, self) # Get notifications when properties are changed self.connect('notify::offset', self.offset_handler) self.connect('notify::timezone-method', self.timezone_handler) self.connect('notify::timezone-city', self.timezone_handler) self.connect('notify::utc-offset', self.timezone_handler) def timezone_handler(self, *ignore): """Set the timezone to the chosen zone and update all photos.""" environ['TZ'] = '' if self.timezone_method == 'lookup': # Note that this will gracefully fallback on system timezone # if no timezone has actually been found yet. environ['TZ'] = self.found_timezone elif self.timezone_method == 'offset': environ['TZ'] = 'UTC%+d' % -self.utc_offset elif self.timezone_method == 'custom' and \ self.timezone_region and self.timezone_city: environ['TZ'] = '/'.join( [self.timezone_region, self.timezone_city]) tzset() self.offset_handler() def offset_handler(self, *ignore): """When the offset is changed, update the loaded photos.""" for i, photo in enumerate(self.photos): if not i % 10: Widgets.redraw_interface() photo.calculate_timestamp(self.offset) def add_photo(self, photo): """Adds photo to the list of photos taken by this camera.""" photo.camera = self self.photos.add(photo) self.notify('num_photos') def remove_photo(self, photo): """Removes photo from the list of photos taken by this camera.""" photo.camera = None self.photos.discard(photo) self.notify('num_photos')