I've imported lots of pictures into Shotwell and spent some time setting tags. Are those tags locked-in in Shotwell or I can export them (and import again or use with other software)?

6 Answers 6


I'm the founder of Yorba, makers of Shotwell. Thanks for your question.

Shotwell 0.7 writes metadata (such as tags and titles) to photos when you export them. The metadata is written in EXIF, IPTC and/or XMP format (depending on which of these were present in the photo to begin with). Most other photo programs can read these formats, so if you export your photos from Shotwell then other programs should be able to read their tags with no problem.

The upcoming Shotwell 0.8 can write metadata to photo files on the fly - to enable this, select the option "Write tags, titles and other metadata to photo files" in the preferences dialog. Once this is selected, Shotwell will update metadata in photo files as soon as you tag them. To use this feature, build the Shotwell trunk from source (see http://yorba.org/shotwell/install/#source), or just wait for Shotwell 0.8 (which we plan to release later in December).

  • +1 I'm a new user of Shotwell; thanks for all your fine work.
    – msw
    Dec 26, 2010 at 2:44
  • 3
    Does the on the fly meta tag writing work retroactive?
    – hultqvist
    Mar 25, 2011 at 8:28
  • @hultqvist, yes, I just checked the option and shotwell started writing it to all my photos afterwards
    – some user
    Oct 19, 2015 at 20:09

Unfortunately Shotwell does seem to keep the tags in its own database rather than embed them as exif, IPTC or XMP in the pictures. You can check by using exiftool, which can be installed by installing the package libimage-exiftool-perl, available in the repositories.

See some examples here

use the command; exiftool testpicture.jpg to check a photo called testpicture.jpg that you had previously tagged with Shotwell. You will see that the exiftool output contains no Shotwell tags.

The exiftool utility can tag your pictures embedding the tags in the photo and the good thing about this is that most photo managers will use them, this includes Shotwell. For example:

exiftool -keywords=favourite -keywords=family testpicture.jpg

Replace existing keyword list with two new keywords (favourite and family).

When testpicture.jpg is imported into Shotwell the picture will be tagged with favourite and family

It may be helpful to know that the Shotwell database is an sqlite database located in your; ~/.shotwell/data directory and usually called photo.db, you can copy it somewhere else on your computer and access it with sqlite.

There are a few GUI frontends for sqlite, there is one for firefox here or you can use sqliteman. Both of these front ends have export to csv features; when you export your tags to csv (Comma Separated Values) you can check if any other photo management software will import and map the tags to the appropriate field in their own databases. I believe Digikam can do this. Digikam can also embed exif data in the photos themselves.

Hopefully as Shotwell gains more features this situation will change.

UPDATE: While it is true that Shotwell 0.7 does not store its tags in the pictures as these tags are created, the tags can be embedded in the pictures if you choose to export them, thanks Adam for making this clear. Hopefully this export is lossless when dealing with jpegs. I suspect it is, if one selects original size for the Scaling option in the export dialog.

  • 1
    During export if the Scaling option is Original Size and the photo is unedited (no color adjustments, crop, etc.), it's lossless. Note that changing the orientation of a photo is lossless as well, as we use the EXIF flag rather than re-encoding the rotated image.
    – Jim Nelson
    Feb 21, 2011 at 20:21
  • I believe you can check the "Write tags, titles, and other metadata to photo files" box in Shetwell preferences to have it write its tags out to the image files. Mar 14, 2015 at 4:16
  • Fortunately it doesn't modify files. It is very dumb to modify files. Once the exif is modified I can not rely that the file has not been changed. What if the files actually was corrupted? No the right approach is not to do any changes. All changes should be in separate db. Jun 5, 2020 at 12:40

Quick (dirty?) python code to do this without upgrading Shotwell (I think as of 0.8.x Shotwell can write out tags, but you can't upgrade to that on Lucid). This thing will write out star-ratings as tags (comment that bit out, obviously, if you don't want that).

Requires exiftool. It'll duplicate any tags that are both in the shotwell database AND the images (ie ones that Shotwell imported when it imported the images) so watch out for that. Also, takes quite a while for a big collection of photos.

import os
conn = sqlite3.connect("/home/  username  /.shotwell/data/photo.db")

def get_tags():
    return [ x[0] for x in conn.execute("SELECT name FROM TagTable").fetchall()]

def tag_query(tag):
    return conn.execute("SELECT photo_id_list FROM TagTable WHERE name=?", (tag,)).fetchone()[0].split(",")

def get_tagged_photos(tag):
    for id in tag_query(tag):
        result = conn.execute("select filename from PhotoTable where id=?", (id,) ).fetchone()
        if result:
            yield result[0]

def get_photos_by_rating(rating):
    return [photo[0] for photo in conn.execute("select filename from PhotoTable where rating=?",(rating,)).fetchall()]

def get_tagging_commands():
    commands = []
    for rating in range(1,5):
        for photo in get_photos_by_rating(rating):
             commands.append("exiftool -overwrite_original_in_place -preserve -keywords+=rating%d \"%s\""% (rating,photo))

    for tag in [tag for tag in get_tags() if tag != "keep"]:
        for photo in get_tagged_photos(tag):
             commands.append("exiftool -overwrite_original_in_place -preserve -keywords+=%s \"%s\"" % (tag,photo))

    return commands

commands = get_tagging_commands()
for command in commands:
    print command

If you want a really good GUI tool/browser that will let you tag your images using Exif tags (and therefore available in Shotwell too), I recommend jBrout.

I've written about jBrout on my blog.

To install it, go to Synaptic, choose settings/repositories, click on the "Other Software" tab, then hit the "Add" button and paste in this line :

deb http://jbrout.free.fr/download/debian binary/

Then reload and search for jBrout.


Since ~/.shotwell/data/photo.db is identified as photo.db: SQLite 3.x database by file command, I used SQLite Database Browser (sqlitebrowser) to open it.

Hmmm... you can read it :-) It has CVS export feature.

This is not normal GUI approach but there is a way.


I tried using user38122's script for parsing the shotwell database, and it didn't work. Apparently the schema has been changed in recent versions. Instead I wrote the following script that uses pandas (which i personally prefer to writing SQL) to do tag intersections. In the example below I show all the images that have both the tag 'cat' and the tag 'sleeping'.


# An example of how to query the shotwell database with pandas
import sqlite3, pandas, os, time, datetime

con = sqlite3.connect('/home/dov/.local/share/shotwell/data/photo.db')
photo_df = pandas.read_sql("SELECT * from PhotoTable", con)

for c in ['exposure_time','timestamp','time_created']:
  photo_df[c] = photo_df[c].map(datetime.datetime.fromtimestamp)

tag_df = pandas.read_sql('SELECT * from TagTable', con)

def get_image_ids(tag):
  """The image ids are stored morphed in the database as %016x"""
  global tag_df

  return set([int(s.replace('thumb',''),16)
              for s in tag_df[tag_df.name==tag].photo_id_list.iloc[0].split(',')
              if len(s)])

def get_photos(ids):
  """Get the photos for a list of ids"""
  global photo_df
  return photo_df[photo_df.id.isin(ids)].sort(['exposure_time'])

def view_pix(rows):
  cmd = ('eog ' + ' '.join(['"%s"'%row.filename
                            for idx,row in rows.iterrows()]))
#  print cmd

print 'querying...'

# An example of how to create an intersection of two tags
ids1 = get_image_ids('cat')
ids2 = get_image_ids('sleeping')
rows = get_photos(ids1.intersection(ids2))

# An example of how to filter the rows by timestamp
time_low,time_high = datetime.datetime(2006,8,1),datetime.datetime(2009,1,1)
rows = rows[(rows.exposure_time > time_low)
            & (rows.exposure_time < time_high)]
print '\n'.join([str(ts) for ts in rows['exposure_time']])

print 'done'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .