How to use file path for icon in export plugin?

Hello, I am writing an export plugin that I would like to customise easily. This includes changing the icon used. The icon file name is set at self.icon in the settings method of the principal plugin class. However, I can only set the file name, not a path.

Ideally, I would like to keep my icons in a separate directory. I tried setting something like self.icon = "img/icon" instead of self.icon = "icon", but that didn’t work. Any tips? Thanks!

You can load the image manually as an NSImage and set the name of the image to something specific (best to prefix the name with your plug-in’s name). Then, provide that name.

from Foundation import NSBundle
from AppKit import NSImage

# ...

plugin_class = objc.lookUpClass("MyPluginClass")
bundle = NSBundle.bundleForClass_(plugin_class)
some_image_path = bundle.pathForResource_ofType_inDirectory_(
    "icon",
    "pdf",
    "img"
)
some_image = NSImage.alloc().initByReferencingFile_(some_image_path)
icon_name = "MyPluginIcon"
some_image.setName_(icon_name)
self.icon_image = some_image
self.icon = icon_name

I have not tried this code, but the technique should be along these lines.

I’m not sure I understand your answer correctly. In case I do, I think you misunderstood my question: I want to set a path to my icon file, not the icon name.

Currently, it’s not a problem to do the following:

self.icon = "my_icon_name"

However, it’s not possible to reference this icon name if it’s in a different directory, like this:

self.icon = "path/to/my_icon_name"

Is this a limitation of the file format plugin, or am I missing something?

The way that API works is it’s using the name of an image for the icon.

If the system finds no image for that name, it looks for image files with that name. This file name cannot contain a slash. So, the solution is to register an image name manually, loading the file from the correct paths yourself.

I might be wrong about some specifics, but my reading of the code that handles this tells me that registering an image by name manually should work.


Edit: fixed that the image needs to be loaded manually also from the path.

Setting image.name registers it with NSImage and it can be accessed by NSImage.imageNamed_(). You just need to store the image in an instance variable to make sure it stays around. For images in the main bundle you can use the file name and NSImage will find it. But for plugins, that doesn’t work.

Thanks a lot for your explanations. I’m afraid I’m a bit too stupid to understand it entirely, I tried the following:

class MyPlugin(FileFormatPlugin):
    objc.python_method
    def settings(self):
        self.set_plugin_icon()

    # ...

    @objc.python_method
    def set_plugin_icon(self):
        plugin_class = objc.lookUpClass(self.__class__.__name__)
		bundle = NSBundle.bundleForClass_(plugin_class)
		icon_path = bundle.pathForResource_ofType_inDirectory_(
			"my_image_name",
			"tif",
			"img"
		)
		my_image = NSImage.alloc().initByReferencingFile_(icon_path)
		icon_name = "my_image_name"
		some_image.setName_(icon_name)
		self.icon_image = my_image
		self.icon = icon_name

For the image called my_image_name.tif, in the img directory right next to plugin.py.
I get:

some_image.setName_(icon_name)
^^^^^^^^^^^^^^^^^^^

AttributeError: 'NoneType' object has no attribute 'setName_'

Any idea what I’m doing wrong?

The FileFormat Plugins icon code is a bit strange. Didn’t look at it in a while. You can only set the image by self.icon = icon_name and the image needs to be next to the plugin.py file.
I improved the code to be more flexible. Will be better wit the next version.

Thanks a lot for the clarification!

How will this work in the next version? Can I specify a path directly, or do I need to register an NSImage like explained previously?

I found a way to get it to work in older versions, too:

icon_path = os.path.join(os.path.dirname(self.__file__()), "Images/IconTemplate.pdf")
self.toolbarIcon = NSImage.alloc().initByReferencingFile_(icon_path)
self.toolbarIcon.setName_(self.toolbarIconName())

Wow, that works! Thank you!

But why not just put the icon in the Resource folder?

For easier file management. I have my plugin set up in a way that I set a config name, and it then loads with custom options for everything, including a different icon. Having 6 icons in addition to 6+ python files in the Resources folder gets quite cluttered.