[postr] Use GIO GFiles instead of python file objects (#518273)
- From: Germán Poó Caamaño <gpoo src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [postr] Use GIO GFiles instead of python file objects (#518273)
- Date: Mon, 14 Dec 2009 20:56:56 +0000 (UTC)
commit ac20120f109ee029a09761dfe810fecff4ec1c5d
Author: David Ignacio <deignacio gmail com>
Date: Mon Dec 14 17:52:44 2009 -0300
Use GIO GFiles instead of python file objects (#518273)
This allow users to upload files that exist on remote systems,
such as SMB. Previously only files on local filesystems could
be loaded.
Signed-off-by: Germán Póo-Caamaño <gpoo gnome org>
src/ImageStore.py | 4 +-
src/flickrest.py | 24 +++++-----
src/postr.py | 137 +++++++++++++++++++++++++++++++++++-----------------
3 files changed, 106 insertions(+), 59 deletions(-)
---
diff --git a/src/ImageStore.py b/src/ImageStore.py
index b58eb39..523c064 100644
--- a/src/ImageStore.py
+++ b/src/ImageStore.py
@@ -18,7 +18,7 @@
import gobject, gtk
# Column indexes
-(COL_FILENAME, # The filename of an image (can be None)
+(COL_URI, # The filename of an image (can be None)
COL_SIZE, # Integer, file size
COL_IMAGE, # The image data (if filename is None)
COL_PREVIEW, # A 512x512 preview of the image
@@ -37,7 +37,7 @@ import gobject, gtk
class ImageStore (gtk.ListStore):
def __init__(self):
- gtk.ListStore.__init__(self, gobject.TYPE_STRING, # COL_FILENAME
+ gtk.ListStore.__init__(self, gobject.TYPE_STRING, # COL_URI
gobject.TYPE_INT, # COL_SIZE
gtk.gdk.Pixbuf, # COL_IMAGE
gtk.gdk.Pixbuf, # COL_PREVIEW
diff --git a/src/flickrest.py b/src/flickrest.py
index 23b0a95..fb298f3 100644
--- a/src/flickrest.py
+++ b/src/flickrest.py
@@ -16,6 +16,7 @@
# St, Fifth Floor, Boston, MA 02110-1301 USA
import logging, os, mimetools, urllib
+import gio
from twisted.internet import defer
from twisted.python.failure import Failure
import proxyclient as client
@@ -144,16 +145,15 @@ class Flickr:
for key, val in inputs.items():
lines.append("--" + boundary.encode("utf-8"))
header = 'Content-Disposition: form-data; name="%s";' % key
- # Objects with name value are probably files
- if hasattr(val, 'name'):
- header += 'filename="%s";' % os.path.split(val.name)[1]
+ if isinstance(val, gio.File):
+ header += 'filename="%s";' % val.get_basename()
lines.append(header)
header = "Content-Type: application/octet-stream"
lines.append(header)
lines.append("")
- # If there is a read attribute, it is a file-like object, so read all the data
- if hasattr(val, 'read'):
- lines.append(val.read())
+ if isinstance(val, gio.File):
+ contents, length, etags = val.load_contents()
+ lines.append(contents)
# Otherwise just hope it is string-like and encode it to
# UTF-8. TODO: this breaks when val is binary data.
else:
@@ -162,15 +162,15 @@ class Flickr:
lines.append("--" + boundary.encode("utf-8"))
return (boundary, '\r\n'.join(lines))
- def upload(self, filename=None, imageData=None,
+ def upload(self, uri=None, imageData=None,
title=None, desc=None, tags=None,
is_public=None, is_family=None, is_friend=None,
safety=None, search_hidden=None, content_type=None):
# Sanity check the arguments
- if filename is None and imageData is None:
- raise ValueError("Need to pass either filename or imageData")
- if filename and imageData:
- raise ValueError("Cannot pass both filename and imageData")
+ if uri is None and imageData is None:
+ raise ValueError("Need to pass either uri or imageData")
+ if uri and imageData:
+ raise ValueError("Cannot pass both uri and imageData")
kwargs = {}
if title:
@@ -197,7 +197,7 @@ class Flickr:
if imageData:
kwargs['photo'] = imageData
else:
- kwargs['photo'] = file(filename, "rb")
+ kwargs['photo'] = gio.File(uri)
(boundary, form) = self.__encodeForm(kwargs)
headers= {
diff --git a/src/postr.py b/src/postr.py
index 12bfd58..de292f7 100644
--- a/src/postr.py
+++ b/src/postr.py
@@ -20,7 +20,7 @@ from urlparse import urlparse
from os.path import basename
import pygtk; pygtk.require ("2.0")
-import gobject, gtk, gtk.glade, gconf
+import gobject, gtk, gtk.glade, gconf, gio
try:
import gtkspell
@@ -55,6 +55,10 @@ except ImportError:
ROTATED_90_CCW
) = (1, 3, 6, 8)
+_FILE_ATTRIBUTES = ",".join([gio.FILE_ATTRIBUTE_STANDARD_TYPE,
+ gio.FILE_ATTRIBUTE_STANDARD_NAME,
+ gio.FILE_ATTRIBUTE_STANDARD_SIZE,
+ gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME])
class Postr(UniqueApp):
def __init__(self):
@@ -399,7 +403,7 @@ class Postr(UniqueApp):
gtk.RESPONSE_OK))
dialog.set_select_multiple(True)
if self.last_folder:
- dialog.set_current_folder(self.last_folder)
+ dialog.set_current_folder_uri(self.last_folder)
# Add filters for all reasonable image types
filters = gtk.FileFilter()
@@ -427,9 +431,9 @@ class Postr(UniqueApp):
if dialog.run() == gtk.RESPONSE_OK:
dialog.hide()
- self.last_folder = dialog.get_current_folder()
- for f in dialog.get_filenames():
- self.add_image_filename(f)
+ self.last_folder = dialog.get_current_folder_uri()
+ for uri in dialog.get_uris():
+ self.add_image_uri(uri)
dialog.destroy()
def on_quit_activate(self, widget, *args):
@@ -669,47 +673,94 @@ class Postr(UniqueApp):
self.thumbnail_image.set_from_icon_name("postr", self.logo_icon_size)
[obj.handler_unblock(i) for obj,i in self.change_signals]
- def add_image_filename(self, filename):
+ def add_image_dir_file(self, gfile):
+ children = gfile.enumerate_children(_FILE_ATTRIBUTES, flags=gio.FILE_QUERY_INFO_NONE)
+ child_info = children.next_file()
+ while child_info:
+ file_type = child_info.get_file_type()
+ if file_type == gio.FILE_TYPE_REGULAR:
+ self.add_image_fileinfo(gfile, child_info)
+ elif file_type == gio.FILE_TYPE_DIRECTORY:
+ dirname = os.path.join(gfile.get_uri(), child_info.get_name())
+ self.add_image_dir_file(gio.File(dirname))
+ else:
+ print "Unhandled file %s" % gfile.get_uri()
+ child_info = children.next_file()
+ children.close()
+
+ def add_image_uri(self, uri):
"""Add a file to the image list. Called by the File->Add Photo and drag
and drop callbacks."""
- # TODO: MIME type check
+ return self.add_image_filename(uri)
- # Check the file size
- try:
- filesize = os.path.getsize(filename)
- except os.error:
- d = ErrorDialog(self.window)
- d.set_from_string(_("File at %s does not exist or is currently inaccessible.") % filename)
- d.show_all()
- return
+ def add_image_filename(self, filename):
+ gfile = gio.File(filename)
+ fileinfo = gfile.query_info(_FILE_ATTRIBUTES, flags=gio.FILE_QUERY_INFO_NONE)
+ self.add_image_file(gfile, fileinfo)
+
+ def add_image_fileinfo(self, parent, fileinfo):
+ filename = os.path.join(parent.get_uri(), fileinfo.get_name())
+ gfile = gio.File(filename)
+ self.add_image_file(gfile, fileinfo)
+
+ def _on_preview_size_prepared(self, loader, width, height):
+ """Appropriately scale the image preview to fit inside 512x512"""
+ if width > height:
+ new_width = 512
+ new_height = 512 * height / width
+ else:
+ new_width = 512 * width / height
+ new_height = 512
+ loader.set_size(new_width, new_height)
+ def add_image_file(self, gfile, fileinfo):
+ filesize = fileinfo.get_size()
if filesize > self.statusbar.maxfile * 1024 * 1024:
d = ErrorDialog(self.window)
- d.set_from_string(_("Image %s is too large, images must be no larger than %dMB in size.") % (filename, self.statusbar.maxfile))
+ d.set_from_string(_("Image %s is too large, images must be no larger than %dMB in size.") % (gfile.get_path(), self.statusbar.maxfile))
d.show_all()
return
# TODO: we open the file three times now, which is madness, especially
# if gnome-vfs is used to read remote files. Need to find/write EXIF
# and IPTC parsers that are incremental.
-
+
+ # only opening the file_stream
+ file_stream = gfile.read()
+
# First we load the image scaled to 512x512 for the preview.
try:
- preview = gtk.gdk.pixbuf_new_from_file_at_size(filename, 512, 512)
+ if gfile.is_native():
+ preview = gtk.gdk.pixbuf_new_from_file_at_size(gfile.get_path(), 512, 512)
+ else:
+ loader = gtk.gdk.PixbufLoader()
+ loader.connect("size-prepared", self._on_preview_size_prepared)
+ loader.write(file_stream.read())
+ loader.close()
+ preview = loader.get_pixbuf()
except Exception, e:
d = ErrorDialog(self.window)
d.set_from_exception(e)
d.show_all()
return
+ # type 1 is beginning of file
+ if not file_stream.can_seek() or not file_stream.seek(0, type=1):
+ file_stream = gfile.read()
+
# On a file that doesn't contain EXIF, like a PNG, this just returns an
# empty set.
try:
- exif = EXIF.process_file(open(filename, 'rb'))
+ exif = EXIF.process_file(file_stream)
except:
exif = {}
+
+ # type 1 is beginning of file
+ if not file_stream.can_seek() or not file_stream.seek(0, type=1):
+ file_stream = gfile.read()
+
try:
- iptc = IPTCInfo(open(filename, 'rb')).data
+ iptc = IPTCInfo(file_stream).data
except:
iptc = {}
@@ -752,12 +803,12 @@ class Postr(UniqueApp):
return value
return default
- title = slurp(title_tags, os.path.splitext(os.path.basename(filename))[0])
+ title = slurp(title_tags, os.path.splitext(fileinfo.get_display_name())[0])
desc = slurp(desc_tags)
tags = slurp(tag_tags)
self.model.set(self.model.append(),
- ImageStore.COL_FILENAME, filename,
+ ImageStore.COL_URI, gfile.get_uri(),
ImageStore.COL_SIZE, filesize,
ImageStore.COL_IMAGE, None,
ImageStore.COL_PREVIEW, preview,
@@ -791,7 +842,7 @@ class Postr(UniqueApp):
self.model.set(self.model.append(),
ImageStore.COL_IMAGE, pixbuf,
ImageStore.COL_SIZE, size,
- ImageStore.COL_FILENAME, None,
+ ImageStore.COL_URI, None,
ImageStore.COL_PREVIEW, preview,
ImageStore.COL_THUMBNAIL, thumb,
ImageStore.COL_TITLE, "",
@@ -802,19 +853,15 @@ class Postr(UniqueApp):
elif targetType == ImageList.DRAG_URI:
for uri in selection.get_uris():
- # TODO: use gnome-vfs to handle remote files
- filename = urllib.unquote(urlparse(uri)[2])
- if os.path.isfile(filename):
- self.add_image_filename(filename)
- elif os.path.isdir(filename):
- for root, dirs, files in os.walk(filename):
- for f in files:
- # TODO: handle symlinks to directories as they are
- # in files
- self.add_image_filename(os.path.join(root, f))
+ gfile = gio.File(uri)
+ fileinfo = gfile.query_info(_FILE_ATTRIBUTES)
+ file_type = fileinfo.get_file_type()
+ if file_type == gio.FILE_TYPE_REGULAR:
+ self.add_image_file(gfile, fileinfo)
+ elif file_type == gio.FILE_TYPE_DIRECTORY:
+ self.add_image_dir_file(gfile)
else:
- print "Unhandled file %s" % filename
-
+ print "Unhandled file %s" % gfile.get_uri()
else:
print "Unhandled target type %d" % targetType
@@ -900,10 +947,10 @@ class Postr(UniqueApp):
self.upload_done()
return
- (filename, thumb, pixbuf, title, desc,
+ (uri, thumb, pixbuf, title, desc,
tags, set_it, groups, privacy_it, safety_it,
visible, content_type_it, license_it) = self.model.get(it,
- ImageStore.COL_FILENAME,
+ ImageStore.COL_URI,
ImageStore.COL_THUMBNAIL,
ImageStore.COL_IMAGE,
ImageStore.COL_TITLE,
@@ -948,12 +995,12 @@ class Postr(UniqueApp):
else:
license = None
- self.update_progress(filename, title, thumb)
+ self.update_progress(uri, title, thumb)
self.upload_index += 1
self.current_upload_it = it
- if filename:
- d = self.flickr.upload(filename=filename,
+ if uri:
+ d = self.flickr.upload(uri=uri,
title=title, desc=desc,
tags=tags, search_hidden=not visible, safety=safety,
is_public=is_public, is_family=is_family, is_friend=is_friend,
@@ -1082,7 +1129,7 @@ class Postr(UniqueApp):
dialog.destroy()
def _marshal_row(self, path, iter):
- (filename,
+ (uri,
title,
desc,
tags,
@@ -1091,7 +1138,7 @@ class Postr(UniqueApp):
privacy_it,
safety_it,
visible) = self.model.get(iter,
- ImageStore.COL_FILENAME,
+ ImageStore.COL_URI,
ImageStore.COL_TITLE,
ImageStore.COL_DESCRIPTION,
ImageStore.COL_TAGS,
@@ -1119,7 +1166,7 @@ class Postr(UniqueApp):
safety_path = None
args = ( path,
- filename,
+ uri,
title,
desc,
tags,
@@ -1219,9 +1266,9 @@ class Postr(UniqueApp):
dialog.destroy()
def _unmarshal_and_import_row(self, index, row, should_ignore_photosets):
- (path, filename, title, desc, tags, set_id, groups, privacy_path, safety_path, visible) = row
+ (path, uri, title, desc, tags, set_id, groups, privacy_path, safety_path, visible) = row
- self.add_image_filename(filename)
+ self.add_image_uri(uri)
self._set_value_in_model(ImageStore.COL_TITLE, title, [index])
self._set_value_in_model(ImageStore.COL_DESCRIPTION, desc, [index])
self._set_value_in_model(ImageStore.COL_TAGS, tags, [index])
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]