def load_gpx_from_file(self, uri): """Parse GPX data, drawing each GPS track segment on the map.""" start_time = clock() open_file = KMLFile if uri[-3:].lower() == 'kml' else GPXFile gpx = open_file(uri, self.progressbar) # Emitting this signal ensures the new tracks get the correct color. get_obj('colorselection').emit('color-changed') self.status_message( _('%d points loaded in %.2fs.') % (len(gpx.tracks), clock() - start_time), True) if len(gpx.tracks) < 2: return points.update(gpx.tracks) metadata.alpha = min(metadata.alpha, gpx.alpha) metadata.omega = max(metadata.omega, gpx.omega) map_view.emit('realize') map_view.set_zoom_level(map_view.get_max_zoom_level()) bounds = Champlain.BoundingBox.new() for poly in polygons: bounds.compose(poly.get_bounding_box()) gpx.latitude, gpx.longitude = bounds.get_center() map_view.ensure_visible(bounds, False) self.prefs.gpx_timezone = gpx.lookup_geoname() self.prefs.set_timezone() gpx_sensitivity()
def __init__(self): """Start the map at the previous location, and connect signals.""" perform_zoom = lambda button, zoom: zoom() back_button = get_obj('back_button') zoom_in_button = get_obj('zoom_in_button') zoom_out_button = get_obj('zoom_out_button') zoom_out_button.connect('clicked', perform_zoom, map_view.zoom_out) zoom_in_button.connect('clicked', perform_zoom, map_view.zoom_in) back_button.connect('clicked', go_back, map_view) for key in ['latitude', 'longitude', 'zoom-level']: gst.bind(key, map_view, key) accel = Gtk.AccelGroup() window = get_obj('main') window.add_accel_group(accel) for key in ['Left', 'Right', 'Up', 'Down']: accel.connect(Gdk.keyval_from_name(key), Gdk.ModifierType.MOD1_MASK, 0, move_by_arrow_keys) map_view.connect('notify::zoom-level', zoom_button_sensitivity, zoom_in_button.set_sensitive, zoom_out_button.set_sensitive) map_view.connect('realize', remember_location) map_view.connect('animation-completed', set_window_title, window.set_title, Coordinates()) map_view.emit('animation-completed')
def load_gpx_from_file(self, uri): """Parse GPX data, drawing each GPS track segment on the map.""" start_time = clock() open_file = KMLFile if uri[-3:].lower() == 'kml' else GPXFile gpx = open_file(uri, self.progressbar) # Emitting this signal ensures the new tracks get the correct color. get_obj('colorselection').emit('color-changed') self.status_message(_('%d points loaded in %.2fs.') % (len(gpx.tracks), clock() - start_time), True) if len(gpx.tracks) < 2: return points.update(gpx.tracks) metadata.alpha = min(metadata.alpha, gpx.alpha) metadata.omega = max(metadata.omega, gpx.omega) map_view.emit('realize') map_view.set_zoom_level(map_view.get_max_zoom_level()) bounds = Champlain.BoundingBox.new() for poly in polygons: bounds.compose(poly.get_bounding_box()) gpx.latitude, gpx.longitude = bounds.get_center() map_view.ensure_visible(bounds, False) self.prefs.gpx_timezone = gpx.lookup_geoname() self.prefs.set_timezone() gpx_sensitivity()
def __init__(self): """Start the map at the previous location, and connect signals.""" perform_zoom = lambda button, zoom: zoom() back_button = get_obj('back_button') zoom_in_button = get_obj('zoom_in_button') zoom_out_button = get_obj('zoom_out_button') zoom_out_button.connect('clicked', perform_zoom, map_view.zoom_out) zoom_in_button.connect('clicked', perform_zoom, map_view.zoom_in) back_button.connect('clicked', go_back, map_view) for key in ['latitude', 'longitude', 'zoom-level']: gst.bind(key, map_view, key) accel = Gtk.AccelGroup() window = get_obj('main') window.add_accel_group(accel) for key in [ 'Left', 'Right', 'Up', 'Down' ]: accel.connect(Gdk.keyval_from_name(key), Gdk.ModifierType.MOD1_MASK, 0, move_by_arrow_keys) map_view.connect('notify::zoom-level', zoom_button_sensitivity, zoom_in_button.set_sensitive, zoom_out_button.set_sensitive) map_view.connect('realize', remember_location) map_view.connect('animation-completed', set_window_title, window.set_title, Coordinates()) map_view.emit('animation-completed')
def __init__(self): self.selection = get_obj('photos_view').get_selection() self.selection.set_mode(Gtk.SelectionMode.MULTIPLE) self.layer = Champlain.MarkerLayer() map_view.add_layer(self.layer) self.selection.connect('changed', update_highlights) self.selection.connect('changed', selection_sensitivity, *[get_obj(name) for name in ('close_button', 'save_button', 'revert_button', 'jump_button')])
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 __init__(self): self.select_all = get_obj('select_all_button') self.selection = get_obj('photos_view').get_selection() self.selection.set_mode(Gtk.SelectionMode.MULTIPLE) self.layer = Champlain.MarkerLayer() map_view.add_layer(self.layer) self.selection.connect('changed', update_highlights) self.selection.connect( 'changed', selection_sensitivity, *[ get_obj(name) for name in ('apply_button', 'close_button', 'save_button', 'revert_button') ])
def __init__(self): """Make the search box and insert it into the window.""" self.search = None self.results = get_obj('search_results') self.slide_to = map_view.go_to search = get_obj('search_completion') search.set_match_func( lambda c, s, itr, get: self.search(get(itr, LOCATION) or ''), self.results.get_value) search.connect('match-selected', self.search_completed, map_view) entry = get_obj('search_box') entry.connect('changed', self.load_results, self.results.append) entry.connect('icon-release', lambda entry, i, e: entry.set_text('')) entry.connect('activate', self.repeat_last_search, self.results, map_view)
def __init__(self): self.black = Clutter.Box.new(Clutter.BinLayout()) self.black.set_color(Clutter.Color.new(0, 0, 0, 96)) self.label = Clutter.Text() self.label.set_color(Clutter.Color.new(255, 255, 255, 255)) self.xhair = Clutter.Rectangle.new_with_color( Clutter.Color.new(0, 0, 0, 64)) for signal in [ 'latitude', 'longitude' ]: map_view.connect('notify::' + signal, display, get_obj('maps_link'), self.label) map_view.connect('notify::width', lambda view, param, black: black.set_size(view.get_width(), 30), self.black) scale = Champlain.Scale.new() scale.connect_view(map_view) gst.bind('show-map-scale', scale, 'visible') gst.bind('show-map-center', self.xhair, 'visible') gst.bind('show-map-coords', self.black, 'visible') map_view.bin_layout_add(scale, Clutter.BinAlignment.START, Clutter.BinAlignment.END) map_view.bin_layout_add(self.black, Clutter.BinAlignment.START, Clutter.BinAlignment.START) self.black.get_layout_manager().add(self.label, Clutter.BinAlignment.CENTER, Clutter.BinAlignment.CENTER) map_source_menu()
def __init__(self, filename, root, watch): self.filename = filename self.progress = get_obj('progressbar') self.clock = clock() self.append = None self.tracks = {} self.polygons = set() self.parser = XMLSimpleParser(root, watch) self.parser.parse(filename, self.element_start, self.element_end) empty_trackfile_label.hide() points.update(self.tracks) keys = self.tracks.keys() self.alpha = min(keys) self.omega = max(keys) self.latitude = self.tracks[self.alpha].lat self.longitude = self.tracks[self.alpha].lon # TODO find some kind of parent widget that can group these together # to make it easier to get them and insert them into places. builder = Builder('trackfile') self.colorpicker = builder.get_object('colorpicker') self.trash = builder.get_object('unload') self.label = builder.get_object('trackfile_label') self.label.set_text(basename(filename)) self.colorpicker.set_title(basename(filename)) self.colorpicker.connect('color-set', track_color_changed, self.polygons) self.trash.connect('clicked', self.destroy) get_obj('trackfiles_view').attach_next_to( builder.get_object('trackfile_settings'), None, BOTTOM, 1, 1) self.gst = GSettings('trackfile', basename(filename)) if self.gst.get_string('start-timezone') is '': # Then this is the first time this file has been loaded # and we should honor the user-selected global default # track color instead of using the schema-defined default self.gst.set_value('track-color', gst.get_value('track-color')) self.gst.set_string('start-timezone', self.lookup_geoname()) self.gst.bind_with_convert('track-color', self.colorpicker, 'color', lambda x: Gdk.Color(*x), lambda x: (x.red, x.green, x.blue)) self.colorpicker.emit('color-set')
def __init__(self): self.region = region = get_obj('timezone_region') self.cities = cities = get_obj('timezone_cities') pref_button = get_obj('pref_button') for name in tz_regions: region.append(name, name) region.connect('changed', self.region_handler, cities) cities.connect('changed', self.cities_handler) gst.bind('timezone-region', region, 'active') gst.bind('timezone-cities', cities, 'active') self.colorpicker = get_obj('colorselection') gst.bind_with_convert('track-color', self.colorpicker, 'current-color', lambda x: Gdk.Color(*x), lambda x: (x.red, x.green, x.blue)) self.colorpicker.connect('color-changed', self.track_color_changed) pref_button.connect('clicked', self.preferences_dialog, get_obj('preferences'), region, cities, self.colorpicker) self.radios = {} for option in ['system', 'lookup', 'custom']: option += '-timezone' radio = get_obj(option) radio.set_name(option) gst.bind(option, radio, 'active') self.radios[option] = radio radio.connect('clicked', self.radio_handler) gst.bind('custom-timezone', get_obj('custom_timezone_combos'), 'sensitive') gst.bind('use-dark-theme', Gtk.Settings.get_default(), 'gtk-application-prefer-dark-theme') map_source_menu()
def __init__(self, open_files): # Drag source definitons photos_view = get_obj('photos_view') photos_view.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.COPY) photos_view.drag_source_add_text_targets() photos_view.connect('drag-data-get', self.photo_drag_start) # Drag destination defintions photos_view.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) photos_view.drag_dest_add_text_targets() photos_view.connect('drag-data-received', self.photo_drag_end, False) container = get_obj('map_container') container.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) container.drag_dest_add_text_targets() container.connect('drag-data-received', self.photo_drag_end, True) self.external_drag = True self.selection = photos_view.get_selection() self.open_files = open_files
def confirm_quit_dialog(self, *args): """Teardown method, inform user of unsaved files, if any.""" if len(modified) == 0: Gtk.main_quit() return True dialog = get_obj('quit') dialog.format_secondary_markup(self.strings.quit % len(modified)) response = dialog.run() dialog.hide() self.redraw_interface() if response == Gtk.ResponseType.ACCEPT: self.save_all_files() if response != Gtk.ResponseType.CANCEL: Gtk.main_quit() return True
def map_source_menu(): """Load the predefined map sources into a menu the user can use.""" radio_group = [] map_menu = get_obj('map_source_menu') last_source = gst.get_string('map-source-id') gst.bind_with_convert('map-source-id', map_view, 'map-source', MAP_SOURCES.get, lambda x: x.get_id()) menu_item_clicked = (lambda item, mapid: item.get_active() and map_view.set_map_source(MAP_SOURCES[mapid])) for i, source_id in enumerate(sorted(MAP_SOURCES.keys())): source = MAP_SOURCES[source_id] menu_item = Gtk.RadioMenuItem.new_with_label(radio_group, source.get_name()) radio_group.append(menu_item) if last_source == source_id: menu_item.set_active(True) menu_item.connect('activate', menu_item_clicked, source_id) map_menu.attach(menu_item, 0, 1, i, i+1) map_menu.show_all()
def __init__(self): self.black = Clutter.Box.new(Clutter.BinLayout()) self.black.set_color(Clutter.Color.new(0, 0, 0, 96)) self.label = Clutter.Text() self.label.set_color(Clutter.Color.new(255, 255, 255, 255)) self.xhair = Clutter.Rectangle.new_with_color( Clutter.Color.new(0, 0, 0, 64)) for signal in ['latitude', 'longitude']: map_view.connect('notify::' + signal, display, get_obj('maps_link'), self.label) map_view.connect( 'notify::width', lambda view, param, black: black.set_size(view.get_width(), 30), self.black) scale = Champlain.Scale.new() scale.connect_view(map_view) map_view.bin_layout_add(scale, Clutter.BinAlignment.START, Clutter.BinAlignment.END) map_view.bin_layout_add(self.black, Clutter.BinAlignment.START, Clutter.BinAlignment.START) self.black.get_layout_manager().add(self.label, Clutter.BinAlignment.CENTER, Clutter.BinAlignment.CENTER)
from gi.repository import Gtk, Gdk, GLib from re import compile as re_compile from os.path import basename from calendar import timegm from time import clock from gpsmath import Coordinates from common import GSettings, Builder, gst, get_obj from common import map_view, points, metadata BOTTOM = Gtk.PositionType.BOTTOM RIGHT = Gtk.PositionType.RIGHT known_trackfiles = {} empty_trackfile_label = get_obj('empty_trackfile_list') def get_trackfile(uri): """This method caches TrackFile instances.""" if uri not in known_trackfiles: fmt = KMLFile if uri[-3:].lower() == 'kml' else GPXFile known_trackfiles[uri] = fmt(uri) return known_trackfiles[uri] def make_clutter_color(color): """Generate a Clutter.Color from the currently chosen color.""" return Clutter.Color.new( *[x / 256 for x in [color.red, color.green, color.blue, 49152]]) def track_color_changed(selection, polys):
def __init__(self): self.message_timeout_source = None self.progressbar = get_obj('progressbar') self.error = Struct({ 'message': get_obj('error_message'), 'icon': get_obj('error_icon'), 'bar': get_obj('error_bar') }) self.error.bar.connect('response', lambda widget, signal: widget.hide()) self.strings = Struct({ 'quit': get_obj('quit').get_property('secondary-text'), 'preview': get_obj('preview_label').get_text() }) self.liststore = get_obj('loaded_photos') self.liststore.set_sort_column_id(TIMESTAMP, Gtk.SortType.ASCENDING) cell_string = Gtk.CellRendererText() cell_string.set_property('wrap-mode', Pango.WrapMode.WORD) cell_string.set_property('wrap-width', 200) cell_thumb = Gtk.CellRendererPixbuf() cell_thumb.set_property('stock-id', Gtk.STOCK_MISSING_IMAGE) cell_thumb.set_property('ypad', 6) cell_thumb.set_property('xpad', 12) column = Gtk.TreeViewColumn('Photos') column.pack_start(cell_thumb, False) column.add_attribute(cell_thumb, 'pixbuf', THUMB) column.pack_start(cell_string, False) column.add_attribute(cell_string, 'markup', SUMMARY) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) # Deal with multiple selection drag and drop. self.defer_select = False photos_view = get_obj('photos_view') photos_view.connect('button-press-event', self.photoview_pressed) photos_view.connect('button-release-event', self.photoview_released) photos_view.append_column(column) self.drag = DragController(self.open_files) self.navigator = NavigationController() self.search = SearchController() self.labels = LabelController() self.actors = ActorController() about = get_obj('about') about.set_version(REVISION) about.set_program_name(APPNAME) about.set_logo(GdkPixbuf.Pixbuf.new_from_file_at_size( join(PKG_DATA_DIR, PACKAGE + '.svg'), 192, 192)) click_handlers = { 'open_button': [self.add_files_dialog, get_obj('open')], 'save_button': [self.save_all_files], 'close_button': [lambda btn: [p.destroy() for p in selected.copy()]], 'revert_button': [lambda btn: self.open_files( [p.filename for p in modified & selected])], 'about_button': [lambda yes, you_can: you_can.run() and you_can.hide(), about], 'help_button': [lambda *ignore: Gtk.show_uri(Gdk.Screen.get_default(), 'ghelp:gottengeography', Gdk.CURRENT_TIME)], 'jump_button': [self.jump_to_photo], 'apply_button': [self.apply_selected_photos], } for button, handler in click_handlers.items(): get_obj(button).connect('clicked', *handler) # Hide the unused button that appears beside the map source menu. ugly = get_obj('map_source_menu_button').get_child().get_children()[0] ugly.set_no_show_all(True) ugly.hide() accel = Gtk.AccelGroup() window = get_obj('main') window.resize(*gst.get('window-size')) window.connect('delete_event', self.confirm_quit_dialog) window.add_accel_group(accel) window.show_all() save_size = lambda v, s, size: gst.set_window_size(size()) for prop in ['width', 'height']: map_view.connect('notify::' + prop, save_size, window.get_size) accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK, 0, self.confirm_quit_dialog) self.labels.selection.emit('changed') clear_all_gpx() button = get_obj('apply_button') gst.bind('left-pane-page', get_obj('photo_camera_gps'), 'page') gst.bind('use-dark-theme', Gtk.Settings.get_default(), 'gtk-application-prefer-dark-theme') # This bit of magic will only show the apply button when there is # at least one photo loaded that is not manually positioned. # In effect, it allows you to manually drag & drop some photos, # then batch-apply all the rest btn_sense = lambda *x: button.set_sensitive( [photo for photo in photos.values() if not photo.manual]) self.liststore.connect('row-changed', btn_sense) self.liststore.connect('row-deleted', btn_sense) empty = get_obj('empty_photo_list') empty_visible = lambda l, *x: empty.set_visible(l.get_iter_first() is None) self.liststore.connect('row-changed', empty_visible) self.liststore.connect('row-deleted', empty_visible) toolbar = get_obj('photo_btn_bar') bar_visible = lambda l, *x: toolbar.set_visible(l.get_iter_first() is not None) self.liststore.connect('row-changed', bar_visible) self.liststore.connect('row-deleted', bar_visible) get_obj('open').connect('update-preview', self.update_preview, get_obj('preview_label'), get_obj('preview_image'))
from gi.repository import Gio, GObject, Gtk from math import modf as split_float from gettext import gettext as _ from time import tzset from os import environ from territories import tz_regions, get_timezone from common import get_obj, GSettings, Builder from version import PACKAGE BOTTOM = Gtk.PositionType.BOTTOM RIGHT = Gtk.PositionType.RIGHT known_cameras = {} empty_camera_label = get_obj('empty_camera_list') def get_camera(photo): """This method caches Camera instances.""" names = {'Make': 'Unknown Make', 'Model': 'Unknown Camera'} keys = ['Exif.Image.' + key for key in names.keys() + ['CameraSerialNumber']] + ['Exif.Photo.BodySerialNumber'] for key in keys: try: names.update({key.split('.')[-1]: photo.exif[key].value}) except KeyError: pass # Turn a Nikon Wonder Cam with serial# 12345 into '12345_nikon_wonder_cam' camera_id = '_'.join(sorted(names.values())).lower().replace(' ', '_')
def __init__(self): self.progressbar = get_obj('progressbar') self.error = Struct({ 'message': get_obj('error_message'), 'icon': get_obj('error_icon'), 'bar': get_obj('error_bar') }) self.error.bar.connect('response', lambda widget, signal: widget.hide()) self.strings = Struct({ 'quit': get_obj('quit').get_property('secondary-text'), 'preview': get_obj('preview_label').get_text() }) self.liststore = get_obj('loaded_photos') self.liststore.set_sort_column_id(TIMESTAMP, Gtk.SortType.ASCENDING) cell_string = Gtk.CellRendererText() cell_thumb = Gtk.CellRendererPixbuf() cell_thumb.set_property('stock-id', Gtk.STOCK_MISSING_IMAGE) cell_thumb.set_property('ypad', 6) cell_thumb.set_property('xpad', 12) column = Gtk.TreeViewColumn('Photos') column.pack_start(cell_thumb, False) column.add_attribute(cell_thumb, 'pixbuf', THUMB) column.pack_start(cell_string, False) column.add_attribute(cell_string, 'markup', SUMMARY) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) get_obj('photos_view').append_column(column) self.drag = DragController(self.open_files) self.navigator = NavigationController() self.search = SearchController() self.prefs = PreferencesController() self.labels = LabelController() self.actors = ActorController() about = get_obj('about') about.set_version(REVISION) about.set_program_name(APPNAME) about.set_logo(GdkPixbuf.Pixbuf.new_from_file_at_size( join(PKG_DATA_DIR, PACKAGE + '.svg'), 192, 192)) click_handlers = { 'open_button': [self.add_files_dialog, get_obj('open')], 'save_button': [self.save_all_files], 'clear_button': [clear_all_gpx], 'close_button': [self.close_selected_photos], 'revert_button': [self.revert_selected_photos], 'about_button': [lambda b, d: d.run() and d.hide(), about], 'apply_button': [self.apply_selected_photos, map_view], 'select_all_button': [toggle_selected_photos, self.labels.selection] } for button, handler in click_handlers.items(): get_obj(button).connect('clicked', *handler) accel = Gtk.AccelGroup() window = get_obj('main') window.resize(*gst.get('window-size')) window.connect('delete_event', self.confirm_quit_dialog) window.add_accel_group(accel) window.show_all() # Hide the unused button that appears beside the map source menu. get_obj('map_source_menu_button').get_child().get_children()[0].set_visible(False) save_size = lambda v, s, size: gst.set_window_size(size()) for prop in ['width', 'height']: map_view.connect('notify::' + prop, save_size, window.get_size) accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK, 0, self.confirm_quit_dialog) self.labels.selection.emit('changed') clear_all_gpx() metadata.delta = 0 self.secbutton, self.minbutton = get_obj('seconds'), get_obj('minutes') for spinbutton in [ self.secbutton, self.minbutton ]: spinbutton.connect('value-changed', self.time_offset_changed) gst.bind('offset-minutes', self.minbutton, 'value') gst.bind('offset-seconds', self.secbutton, 'value') gst.bind('left-pane-page', get_obj('photo_camera_gps'), 'page') get_obj('open').connect('update-preview', self.update_preview, get_obj('preview_label'), get_obj('preview_image'))
def __init__(self): self.progressbar = get_obj('progressbar') self.error = Struct({ 'message': get_obj('error_message'), 'icon': get_obj('error_icon'), 'bar': get_obj('error_bar') }) self.error.bar.connect('response', lambda widget, signal: widget.hide()) self.strings = Struct({ 'quit': get_obj('quit').get_property('secondary-text'), 'preview': get_obj('preview_label').get_text() }) self.liststore = get_obj('loaded_photos') self.liststore.set_sort_column_id(TIMESTAMP, Gtk.SortType.ASCENDING) cell_string = Gtk.CellRendererText() cell_thumb = Gtk.CellRendererPixbuf() cell_thumb.set_property('stock-id', Gtk.STOCK_MISSING_IMAGE) cell_thumb.set_property('ypad', 6) cell_thumb.set_property('xpad', 12) column = Gtk.TreeViewColumn('Photos') column.pack_start(cell_thumb, False) column.add_attribute(cell_thumb, 'pixbuf', THUMB) column.pack_start(cell_string, False) column.add_attribute(cell_string, 'markup', SUMMARY) column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) get_obj('photos_view').append_column(column) self.drag = DragController(self.open_files) self.navigator = NavigationController() self.search = SearchController() self.prefs = PreferencesController() self.labels = LabelController() self.actors = ActorController() about = get_obj('about') about.set_version(REVISION) about.set_program_name(APPNAME) about.set_logo( GdkPixbuf.Pixbuf.new_from_file_at_size( join(PKG_DATA_DIR, PACKAGE + '.svg'), 192, 192)) click_handlers = { 'open_button': [self.add_files_dialog, get_obj('open')], 'save_button': [self.save_all_files], 'clear_button': [clear_all_gpx], 'close_button': [self.close_selected_photos], 'revert_button': [self.revert_selected_photos], 'about_button': [lambda b, d: d.run() and d.hide(), about], 'apply_button': [self.apply_selected_photos, map_view], 'select_all_button': [toggle_selected_photos, self.labels.selection] } for button, handler in click_handlers.items(): get_obj(button).connect('clicked', *handler) accel = Gtk.AccelGroup() window = get_obj('main') window.resize(*gst.get('window-size')) window.connect('delete_event', self.confirm_quit_dialog) window.add_accel_group(accel) window.show_all() # Hide the unused button that appears beside the map source menu. get_obj('map_source_menu_button').get_child().get_children( )[0].set_visible(False) save_size = lambda v, s, size: gst.set_window_size(size()) for prop in ['width', 'height']: map_view.connect('notify::' + prop, save_size, window.get_size) accel.connect(Gdk.keyval_from_name('q'), Gdk.ModifierType.CONTROL_MASK, 0, self.confirm_quit_dialog) self.labels.selection.emit('changed') clear_all_gpx() metadata.delta = 0 self.secbutton, self.minbutton = get_obj('seconds'), get_obj('minutes') for spinbutton in [self.secbutton, self.minbutton]: spinbutton.connect('value-changed', self.time_offset_changed) gst.bind('offset-minutes', self.minbutton, 'value') gst.bind('offset-seconds', self.secbutton, 'value') gst.bind('left-pane-page', get_obj('photo_camera_gps'), 'page') get_obj('open').connect('update-preview', self.update_preview, get_obj('preview_label'), get_obj('preview_image'))