The motivation of this PR is to make it easy for Python workbench cache its own icons (possibly generated at runtime), saving the trouble of pre-compiling the icons as binary resources.
* Gui.addIcon(name,content,format='XPM') now support a third argument as format. Default format is 'XPM' as before. The intention is to allow user to cache other format of icon image (which most likely dynamically generated), like 'PNG'. addIcon() still supports file path input (in 'content') just as before.
* A new function Gui.isIconCached(key) is added to check if an icon is cached with the given key.
* ViewProviderPythonFeature::getIcon() (i.e. the python method getIcon() in ViewObject class) now checks if the given string argument is a key to a cached icon. The key will be the 'name' argument you passed to Gui.addIcon().
BTW, I previously added support of passing direct QIcon Python object as 'content' in Gui.addIcon(). But this requires the FreeCAD to be compiled with Pyside/Shiboken support. The recent trouble of finding Pyside2/Shiboken2 proves that this method is unreliable. The following sample code shows how to use the new icon capability and also maintain backward compatibility, i.e. it works on FreeCAD before and after this PR.
Code: Select all
import FreeCADGui
from PySide.QtGui import QIcon, QPainter, QPixmap
from PySide.QtCore import Qt, QIODevice, QBuffer, QByteArray
def getIcon(icon_path, overlay_path=None, size=(16,16), mode=QIcon.Normal, cache=True):
'''
Make an icon that are useable by FreeCAD, with optional overlay
icon_path: file path to the normal icon image
overlay_path: optional file path to an overlay image
size: icon image size
mode: icon mode to use from input icon image
cache: whether to force cache icon on older FreeCAD. Note that if the icon
is intended for view object tree icon, you should not cache the icon,
as it is not supported. Workbench icon does support cache.
Return a key for the cached overlay icon. For older version FreeCAD, return
the file path if no overlay, or else return a string containing the XPM
format of the icon.
Example usage: to obtain an overlayed disabled icon,
getIcon(icon_path, overlay_path, mode=QIcon.Disabled)
'''
# The key for the generated icon. You can change to whatever you like, but
# make it unique among the entire application with other workbenches.
key = icon_path
if overlay_path:
key += overlay_path
fmt = None
try:
if FreeCADGui.isIconCached(key):
return key
fmt = 'PNG'
except Exception:
# Exception here means it's an older version FreeCAD without isIconCached()
# function. So if there is no overlay, just return the file path. Or
# else, we shall later use XPM format, which will result in a slightly
# uglier icon, but works nevertheless.
if not overlay_path and mode==QIcon.Normal:
return icon_path
# Obtain the normal icon image of the given size
pixmap = QIcon(icon_path).pixmap(*size,mode=mode)
if overlay_path:
# Load the overlay image on disk
overlay = QIcon(overlay_path)
# Paint the overlay to the disabled icon at the center
overlay.paint(QPainter(pixmap),0,0,size[0],size[1],Qt.AlignCenter)
# Construct the buffer for saving icon image
data = QByteArray()
buf = QBuffer(data)
buf.open(QIODevice.WriteOnly)
if fmt:
pixmap.save(buf, fmt)
FreeCADGui.addIcon(key,data.data(),fmt)
else:
pixmap.save(buf, 'XPM')
data = data.data().decode('latin1')
if not cache:
return data
try:
FreeCADGui.addIcon(key,data)
except AssertionError:
# For older FreeCAD, this is the only way to check if the icon is
# cached or not. You may want to cache the returned key in your
# object to avoid repeatedly loading icon from disk.
pass
return key