Example #1
0
def fetch_thumbnail(filename, size=Gst.get_int('thumbnail-size'), orient=1):
    """Load a photo's thumbnail from disk

    >>> fetch_thumbnail('gg/widgets.py')
    Traceback (most recent call last):
    OSError: gg/widgets.py: No thumbnail found.
    >>> type(fetch_thumbnail('demo/IMG_2411.JPG'))
    <class 'gi.repository.GdkPixbuf.Pixbuf'>
    """
    try:
        exif = GExiv2.Metadata(filename)
    except GObject.GError:
        raise OSError('{}: No thumbnail found.'.format(filename))

    with ignored(KeyError, ValueError):
        orient = int(exif['Exif.Image.Orientation'])

    try:
        thumb = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, size, size)
    except GObject.GError:
        try:
            preview = exif.get_preview_properties()
            data = exif.get_preview_image(preview[0]).get_data()
        except (IndexError, GObject.GError):
            raise OSError('{}: No thumbnail found.'.format(filename))

        return GdkPixbuf.Pixbuf.new_from_stream_at_scale(
            Gio.MemoryInputStream.new_from_data(data, None), size, size, True,
            None)

    return ROTATIONS.get(orient, lambda x: x)(thumb)
Example #2
0
def fetch_thumbnail(filename, size=Gst.get_int('thumbnail-size'), orient=1):
    """Load a photo's thumbnail from disk

    >>> fetch_thumbnail('gg/widgets.py')
    Traceback (most recent call last):
    OSError: gg/widgets.py: No thumbnail found.
    >>> type(fetch_thumbnail('demo/IMG_2411.JPG'))
    <class 'gi.repository.GdkPixbuf.Pixbuf'>
    """
    try:
        exif = GExiv2.Metadata(filename)
    except GObject.GError:
        raise OSError('{}: No thumbnail found.'.format(filename))

    with ignored(KeyError, ValueError):
        orient = int(exif['Exif.Image.Orientation'])

    try:
        thumb = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, size, size)
    except GObject.GError:
        try:
            data = exif.get_preview_image().get_data()
        except GObject.GError:
            raise OSError('{}: No thumbnail found.'.format(filename))

        return GdkPixbuf.Pixbuf.new_from_stream_at_scale(
            Gio.MemoryInputStream.new_from_data(data, None),
            size, size, True, None)

    return ROTATIONS.get(orient, lambda x: x)(thumb)
Example #3
0
    def read(self):
        """Discard all state and (re)initialize from disk."""
        self.exif = GExiv2.Metadata(self.filename)
        self.manual = False
        self.modified_timeout = None
        self.latitude = 0.0
        self.longitude = 0.0
        self.altitude = 0.0
        self.timestamp = 0

        self.names = (None, None, None)
        self.geotimezone = ''

        for tag in ('Exif.Photo.DateTimeOriginal',
                    'Exif.Image.DateTimeOriginal',
                    'Exif.Photo.DateTime',
                    'Exif.Image.DateTime'):
            with ignored(TypeError, AttributeError, ValueError):
                self.orig_time = datetime.strptime(
                    self.exif.get(tag),
                    '%Y:%m:%d %H:%M:%S').timetuple()
                break

        self.longitude, self.latitude, self.altitude = self.exif.get_gps_info()

        modified.discard(self)
        self.calculate_timestamp()

        if self.iter is None:
            self.iter = Widgets.loaded_photos.append()
        Widgets.loaded_photos.set_row(self.iter, [self.filename,
                                                  str(self),
                                                  self.thumb,
                                                  self.timestamp])

        # Get the camera info
        self.camera_info = {'Make': '', 'Model': ''}
        keys = ['Exif.Image.' + key for key in list(self.camera_info.keys()) +
                ['CameraSerialNumber']] + ['Exif.Photo.BodySerialNumber']
        for key in keys:
            with ignored(KeyError):
                self.camera_info.update(
                    {key.split('.')[-1]: self.exif[key]})
Example #4
0
    def read(self):
        """Discard all state and (re)initialize from disk."""
        self.exif = GExiv2.Metadata(self.filename)
        self.manual = False
        self.modified_timeout = None
        self.latitude = 0.0
        self.longitude = 0.0
        self.altitude = 0.0
        self.timestamp = 0

        self.names = (None, None, None)
        self.geotimezone = ''

        for tag in ('Exif.Photo.DateTimeOriginal',
                    'Exif.Image.DateTimeOriginal', 'Exif.Photo.DateTime',
                    'Exif.Image.DateTime'):
            with ignored(TypeError, AttributeError, ValueError):
                self.orig_time = datetime.strptime(
                    self.exif.get(tag), '%Y:%m:%d %H:%M:%S').timetuple()
                break

        self.longitude, self.latitude, self.altitude = self.exif.get_gps_info()

        modified.discard(self)
        self.calculate_timestamp()

        if self.iter is None:
            self.iter = Widgets.loaded_photos.append()
        Widgets.loaded_photos.set_row(
            self.iter, [self.filename,
                        str(self), self.thumb, self.timestamp])

        # Get the camera info
        self.camera_info = {'Make': '', 'Model': ''}
        keys = [
            'Exif.Image.' + key
            for key in list(self.camera_info.keys()) + ['CameraSerialNumber']
        ] + ['Exif.Photo.BodySerialNumber']
        for key in keys:
            with ignored(KeyError):
                self.camera_info.update({key.split('.')[-1]: self.exif[key]})
Example #5
0
def test_demo_data():
    """Load the demo data and ensure that we're reading it in properly"""
    teardown()
    assert not points
    assert not TrackFile.instances
    assert not TrackFile.range
    Widgets.photos_selection.emit('changed')
    # No buttons should be sensitive yet because nothing's loaded.
    buttons = {}
    for button in ('jump', 'save', 'revert', 'close'):
        buttons[button] = Widgets[button + '_button']
        assert not buttons[button].get_sensitive()

    # Load only the photos first.
    with ignored(OSError):
        TrackFile.load_from_file(IMGFILES[0])
        assert False  # Because it should have raised the exception
    gui.open_files(IMGFILES)

    # Nothing is yet selected or modified, so buttons still insensitive.
    for button in buttons.values():
        assert not button.get_sensitive()

    # Something loaded in the liststore?
    assert len(Widgets.loaded_photos) == 6
    assert Widgets.loaded_photos.get_iter_first()

    assert Photograph.instances
    for photo in Photograph.instances:
        assert not photo in modified
        assert not photo in selected

        # Pristine demo data shouldn't have any tags.
        assert not photo.altitude
        assert not photo.latitude
        assert not photo.longitude
        assert not photo.positioned

        # Add some crap
        photo.latitude = 10.0
        photo.altitude = 650
        photo.longitude = 45.0
        assert photo.positioned

        # photo.read() should discard all the crap we added above.
        # This is in response to a bug where I was using pyexiv2 wrongly
        # and it would load data from disk without discarding old data.
        photo.read()
        photo.lookup_geodata()
        assert not photo.geoname
        assert not photo.altitude
        assert not photo.latitude
        assert not photo.longitude
        assert not photo.positioned
        assert photo.filename == Label(photo).get_name()
        assert Label(photo).photo.filename == Label(photo).get_name()

    # Load the GPX
    with ignored(OSError):
        Photograph.load_from_file(GPXFILES[0])
        assert False  # Because it should have raised the exception
    gui.open_files(GPXFILES)

    # Check that the GPX is loaded
    assert len(points) == 374
    assert len(TrackFile.instances) == 1
    assert TrackFile.range[0] == 1287259751
    assert TrackFile.range[1] == 1287260756

    for photo in Photograph.instances:
        photo.update_derived_properties()

    Widgets.photos_selection.emit('changed')

    # The save button should be sensitive because loading GPX modifies
    # photos, but nothing is selected so the others are insensitive.
    assert buttons['save'].get_sensitive()
    for button in ('jump', 'revert', 'close'):
        assert not buttons[button].get_sensitive()

    assert Photograph.instances
    for photo in Photograph.instances:
        assert photo in modified

        assert photo.latitude
        assert photo.longitude
        assert photo.positioned
        assert Label(photo).get_property('visible')

    # Unload the GPX data.
    TrackFile.clear_all()
    assert not points
    assert not TrackFile.instances
    assert not TrackFile.range

    # Save all photos
    buttons['save'].emit('clicked')
    assert not modified
    for button in ('save', 'revert'):
        assert not buttons[button].get_sensitive()

    Widgets.photos_selection.select_all()
    assert len(selected) == 6
    for button in ('save', 'revert'):
        assert not buttons[button].get_sensitive()
    for button in ('jump', 'close'):
        assert buttons[button].get_sensitive()

    # Close all the photos.
    files = [photo.filename for photo in selected]
    buttons['close'].emit('clicked')
    for button in ('save', 'revert', 'close'):
        assert not buttons[button].get_sensitive()
    assert not Photograph.instances
    assert not modified
    assert not selected

    # Re-read the photos back from disk to make sure that the saving
    # was successful.
    assert len(files) == 6
    for filename in files:
        photo = Photograph(filename)
        photo.read()
        photo.update_derived_properties()
        print(photo.latitude, photo.longitude, photo.altitude)
        assert photo not in modified
        assert photo.positioned
        assert photo.latitude
        assert photo.longitude
        assert photo.altitude > 600
        assert photo.geoname == 'Edmonton, Alberta, Canada'
    assert not modified
    assert len(Photograph.instances) == 6
def test_demo_data():
    """Load the demo data and ensure that we're reading it in properly"""
    teardown()
    assert not points
    assert not TrackFile.instances
    assert not TrackFile.range
    Widgets.photos_selection.emit("changed")
    # No buttons should be sensitive yet because nothing's loaded.
    buttons = {}
    for button in ("jump", "save", "revert", "close"):
        buttons[button] = Widgets[button + "_button"]
        assert not buttons[button].get_sensitive()

    # Load only the photos first.
    with ignored(OSError):
        TrackFile.load_from_file(IMGFILES[0])
        assert False  # Because it should have raised the exception
    gui.open_files(IMGFILES)

    # Nothing is yet selected or modified, so buttons still insensitive.
    for button in buttons.values():
        assert not button.get_sensitive()

    # Something loaded in the liststore?
    assert len(Widgets.loaded_photos) == 6
    assert Widgets.loaded_photos.get_iter_first()

    assert Photograph.instances
    for photo in Photograph.instances:
        assert not photo in modified
        assert not photo in selected

        # Pristine demo data shouldn't have any tags.
        assert not photo.altitude
        assert not photo.latitude
        assert not photo.longitude
        assert not photo.positioned

        # Add some crap
        photo.latitude = 10.0
        photo.altitude = 650
        photo.longitude = 45.0
        assert photo.positioned

        # photo.read() should discard all the crap we added above.
        # This is in response to a bug where I was using pyexiv2 wrongly
        # and it would load data from disk without discarding old data.
        photo.read()
        photo.lookup_geodata()
        assert not photo.geoname
        assert not photo.altitude
        assert not photo.latitude
        assert not photo.longitude
        assert not photo.positioned
        assert photo.filename == Label(photo).get_name()
        assert Label(photo).photo.filename == Label(photo).get_name()

    # Load the GPX
    with ignored(OSError):
        Photograph.load_from_file(GPXFILES[0])
        assert False  # Because it should have raised the exception
    gui.open_files(GPXFILES)

    # Check that the GPX is loaded
    assert len(points) == 374
    assert len(TrackFile.instances) == 1
    assert TrackFile.range[0] == 1287259751
    assert TrackFile.range[1] == 1287260756

    for photo in Photograph.instances:
        photo.update_derived_properties()

    Widgets.photos_selection.emit("changed")

    # The save button should be sensitive because loading GPX modifies
    # photos, but nothing is selected so the others are insensitive.
    assert buttons["save"].get_sensitive()
    for button in ("jump", "revert", "close"):
        assert not buttons[button].get_sensitive()

    assert Photograph.instances
    for photo in Photograph.instances:
        assert photo in modified

        assert photo.latitude
        assert photo.longitude
        assert photo.positioned
        assert Label(photo).get_property("visible")

    # Unload the GPX data.
    TrackFile.clear_all()
    assert not points
    assert not TrackFile.instances
    assert not TrackFile.range

    # Save all photos
    buttons["save"].emit("clicked")
    assert not modified
    for button in ("save", "revert"):
        assert not buttons[button].get_sensitive()

    Widgets.photos_selection.select_all()
    assert len(selected) == 6
    for button in ("save", "revert"):
        assert not buttons[button].get_sensitive()
    for button in ("jump", "close"):
        assert buttons[button].get_sensitive()

    # Close all the photos.
    files = [photo.filename for photo in selected]
    buttons["close"].emit("clicked")
    for button in ("save", "revert", "close"):
        assert not buttons[button].get_sensitive()
    assert not Photograph.instances
    assert not modified
    assert not selected

    # Re-read the photos back from disk to make sure that the saving
    # was successful.
    assert len(files) == 6
    for filename in files:
        photo = Photograph(filename)
        photo.read()
        photo.update_derived_properties()
        print(photo.latitude, photo.longitude, photo.altitude)
        assert photo not in modified
        assert photo.positioned
        assert photo.latitude
        assert photo.longitude
        assert photo.altitude > 600
        assert photo.geoname == "Edmonton, Alberta, Canada"
    assert not modified
    assert len(Photograph.instances) == 6