python - 加速 nautilus python-extensions 以读取图像的 Exif

标签 python gnome nautilus exiftool

我已经编写了一个读取图片元数据的 Nautilus 扩展(执行 exiftool),但是当我打开包含许多文件的文件夹时,它确实会减慢文件管理器的速度并挂起,直到它完成读取文件的元数据数据。

有没有办法让 Nautilus 在运行我的扩展时继续工作?也许 Exif 数据会在我继续工作的过程中逐渐出现在列中。

#!/usr/bin/python

# Richiede:
# nautilus-python
# exiftool
# gconf-python

# Versione 0.15

import gobject
import nautilus
from subprocess import Popen, PIPE
from urllib import unquote
import gconf

def getexiftool(filename):
    options = '-fast2 -f -m -q -q -s3 -ExifIFD:DateTimeOriginal -IFD0:Software -ExifIFD:Flash -Composite:ImageSize -IFD0:Model'
    exiftool=Popen(['/usr/bin/exiftool'] + options.split() + [filename],stdout=PIPE,stderr=PIPE)
    #'-Nikon:ShutterCount' non utilizzabile con l'argomento -fast2
    output,errors=exiftool.communicate()
    return output.split('\n')

class ColumnExtension(nautilus.ColumnProvider, nautilus.InfoProvider, gobject.GObject):
    def __init__(self):
        pass

    def get_columns(self):
        return (
            nautilus.Column("NautilusPython::ExifIFD:DateTimeOriginal","ExifIFD:DateTimeOriginal","Data (ExifIFD)","Data di scatto"),
            nautilus.Column("NautilusPython::IFD0:Software","IFD0:Software","Software (IFD0)","Software utilizzato"),
            nautilus.Column("NautilusPython::ExifIFD:Flash","ExifIFD:Flash","Flash (ExifIFD)","Modalit\u00e0 del flash"),
            nautilus.Column("NautilusPython::Composite:ImageSize","Composite:ImageSize","Risoluzione (Exif)","Risoluzione dell'immagine"),
            nautilus.Column("NautilusPython::IFD0:Model","IFD0:Model","Fotocamera (IFD0)","Modello fotocamera"),
            #nautilus.Column("NautilusPython::Nikon:ShutterCount","Nikon:ShutterCount","Contatore scatti (Nikon)","Numero di scatti effettuati dalla macchina a questo file"),
            nautilus.Column("NautilusPython::Mp","Mp","Megapixel (Exif)","Dimensione dell'immagine in megapixel"),
        )

    def update_file_info_full(self, provider, handle, closure, file):
        client = gconf.client_get_default()

        if not client.get_bool('/apps/nautilus/nautilus-metadata/enable'):
            client.set_bool('/apps/nautilus/nautilus-metadata/enable',0)
            return

        if file.get_uri_scheme() != 'file':
            return

        if file.get_mime_type() in ('image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/x-nikon-nef', 'image/x-xcf', 'image/vnd.adobe.photoshop'):
            gobject.timeout_add_seconds(1, self.update_exif, provider, handle, closure, file)
            return Nautilus.OperationResult.IN_PROGRESS

        file.add_string_attribute('ExifIFD:DateTimeOriginal','')
        file.add_string_attribute('IFD0:Software','')
        file.add_string_attribute('ExifIFD:Flash','')
        file.add_string_attribute('Composite:ImageSize','')
        file.add_string_attribute('IFD0:Model','')
        file.add_string_attribute('Nikon:ShutterCount','')
        file.add_string_attribute('Mp','')

        return Nautilus.OperationResult.COMPLETE

    def update_exif(self, provider, handle, closure, file):
        filename = unquote(file.get_uri()[7:])

        data = getexiftool(filename)

        file.add_string_attribute('ExifIFD:DateTimeOriginal',data[0].replace(':','-',2))
        file.add_string_attribute('IFD0:Software',data[1])
        file.add_string_attribute('ExifIFD:Flash',data[2])
        file.add_string_attribute('Composite:ImageSize',data[3])
        file.add_string_attribute('IFD0:Model',data[4])
        #file.add_string_attribute('Nikon:ShutterCount',data[5])
        width, height = data[3].split('x')
        mp = float(width) * float(height) / 1000000
        mp = "%.2f" % mp
        file.add_string_attribute('Mp',str(mp) + ' Mp')

        Nautilus.info_provider_update_complete_invoke(closure, provider, handle, Nautilus.OperationResult.COMPLETE)

        return false

最佳答案

发生这种情况是因为您正在调用 update_file_info,它是 Nautilus 异步 IO 系统的一部分。因此,如果操作不够快,它会阻止 nautilus。

在您的情况下,它会加剧,因为您正在调用外部程序,这是一项昂贵的操作。请注意,每个文件都会调用一次 update_file_info。如果你有 100 个文件,那么你将调用 100 次外部程序,而 Nautilus 将不得不等待每个文件,然后再处理下一个文件。

自从 nautilus-python 0.7 可用后,update_file_info_fullcancel_update 允许您对异步调用进行编程。您可以查看 documentation of Nautilus 0.7 for more details .

值得一提的是,这只是 nautilus-python 的一个限制,以前没有公开那些在 C 中可用的方法。

编辑:添加了几个示例。

诀窍是使过程尽可能快或使其异步。

示例1:调用外部程序

使用您的代码的简化版本,我们使异步使用 GObject.timeout_add_seconds update_file_info_full

from gi.repository import Nautilus, GObject
from urllib import unquote
from subprocess import Popen, PIPE

def getexiftool(filename):
    options = '-fast2 -f -m -q -q -s3 -ExifIFD:DateTimeOriginal'
    exiftool = Popen(['/usr/bin/exiftool'] + options.split() + [filename],
                     stdout=PIPE, stderr=PIPE)
    output, errors = exiftool.communicate()
    return output.split('\n')

class MyExtension(Nautilus.ColumnProvider, Nautilus.InfoProvider, GObject.GObject):
    def __init__(self):
        pass

    def get_columns(self):
        return (
            Nautilus.Column(name='MyExif::DateTime',
                            attribute='Exif:Image:DateTime',
                            label='Date Original',
                            description='Data time original'
            ),
        )

    def update_file_info_full(self, provider, handle, closure, file_info):
        if file_info.get_uri_scheme() != 'file':
            return

        filename = unquote(file_info.get_uri()[7:])
        attr = ''

        if file_info.get_mime_type() in ('image/jpeg', 'image/png'):
            GObject.timeout_add_seconds(1, self.update_exif, 
                                        provider, handle, closure, file_info)
            return Nautilus.OperationResult.IN_PROGRESS

        file_info.add_string_attribute('Exif:Image:DateTime', attr)

        return Nautilus.OperationResult.COMPLETE

    def update_exif(self, provider, handle, closure, file_info):
        filename = unquote(file_info.get_uri()[7:])

        try:
            data = getexiftool(filename)
            attr = data[0]
        except:
            attr = ''

        file_info.add_string_attribute('Exif:Image:DateTime', attr)

        Nautilus.info_provider_update_complete_invoke(closure, provider, 
                               handle, Nautilus.OperationResult.COMPLETE)
        return False

上面的代码不会阻止 Nautilus,如果列 View 中的“原始日期”列可用,JPEG 和 PNG 图像将显示“未知”值,并且它们会慢慢地正在更新(1 秒后调用子流程)。

示例 2:使用库

与其调用外部程序,还不如使用库。如下例:

from gi.repository import Nautilus, GObject
from urllib import unquote
import pyexiv2

class MyExtension(Nautilus.ColumnProvider, Nautilus.InfoProvider, GObject.GObject):
    def __init__(self):
        pass

    def get_columns(self):
        return (
            Nautilus.Column(name='MyExif::DateTime',
                            attribute='Exif:Image:DateTime',
                            label='Date Original',
                            description='Data time original'
            ),
        )

    def update_file_info_full(self, provider, handle, closure, file_info):
        if file_info.get_uri_scheme() != 'file':
            return

        filename = unquote(file_info.get_uri()[7:])
        attr = ''

        if file_info.get_mime_type() in ('image/jpeg', 'image/png'):
            metadata = pyexiv2.ImageMetadata(filename)
            metadata.read()

            try:
                tag = metadata['Exif.Image.DateTime'].value
                attr = tag.strftime('%Y-%m-%d %H:%M')
            except:
                attr = ''

        file_info.add_string_attribute('Exif:Image:DateTime', attr)

        return Nautilus.OperationResult.COMPLETE

最终,如果例程很慢,您需要使其异步(可能使用比 GObject.timeout_add_seconds 更好的东西。

最后但同样重要的是,在我的示例中,我使用了 GObject Introspection(通常用于 Nautilus 3),但很容易将其更改为直接使用模块 nautilus

关于python - 加速 nautilus python-extensions 以读取图像的 Exif,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9660065/

相关文章:

python - 我不明白使用 np.boxplot 获得 Q1 和 Q3 的结果

python - 为什么在 SqlAlchemy 中使用 "db.*"?

Python subprocess + scp - 无法读取所有输出

linux - 重启确认重置

c - 在 Ubuntu Nautilus 浏览器中打开定位特定文件的文件夹

python - 将数据框中的负值转换为正值 - Python

css - 是否可以在 GNOME 3.24 中消除服务器端窗口标题栏?

svn - 通过 gnome-keyring 为 ssh(仅限)客户端颠覆客户端加密密码

java - 将 Java 应用程序放入文件管理器的上下文菜单中

linux - Meld Nautilus 上下文菜单的 Shell 脚本