iconipy

iconipy

Say goodbye to the tedious hassle of graphic programs! Now you can create stunning icons directly from your code. With the look and feel defined right in the code, adjustments are a breeze. Plus, the included icon sets can easily be expanded with your own sets based on font files. 🎨✨

Installation

pip install iconipy

Usage

First you initialize an "IconFactory" with an icon set and look-and-feel settings like this:

from iconipy import IconFactory 

create_button_icon = IconFactory(
                        icon_set = 'lucide', 
                        icon_size = (128,64), # Providing just an integer would produce a square icon 
                        font_size = 38,  
                        font_color = (0, 0, 0, 255), # Solid black in RGBA format (R, G, B, A)
                        outline_color = 'dimgrey',   # Color names are also supported
                        outline_width = 6,
                        background_color = 'silver', 
                        background_radius = 10
)

Then you create your icons:

icon_home = create_button_icon.asPil('house') # PIL Image
icon_reload = create_button_icon.asQPixmap('refresh-cw') # Qt based frameworks
icon_files = create_button_icon.asTkPhotoImage('files') # tkinter, ttkbootstrap, PySimpleGUI, FreeSimpleGUI
icon_exit_app = create_button_icon.asRawList('log-out') # DearPyGUI

Depending on your GUI toolkit's whims, you can create PIL Image Objects, ByteIO Objects, Byte-Strings, Raw Pixel Lists, TkPhotoImage Objects, QImage Objects, save to file, and more.

You need to preview an icon? Here we go:

create_button_icon.show('house')

The icon sets you can choose from include: lucide, boxicons, ligature_symbols, lineicons, material_icons_regular, material_icons_round_regular, material_icons_sharp_regular, material_icons_outlined_regular, microns and typicons.

Feeling adventurous? Dump all the icons to your hard drive and explore:

create_button_icon.saveAll(<path to target directory>)

You need a hand? Check what the icon set has to offer:

print(create_button_icon.search('hand'))

Just want a list with all icon names? No problem:

print(create_button_icon.icon_names)

More info

Visit https://github.com/digidigital/iconipy or https://iconipy.digidigital.de for sample code for the most popular GUI toolkits and detailed documentation.

1#!/usr/bin/env python3
2"""
3.. include:: ../../README.md
4"""
5from .iconipy import IconFactory, CustomIconFactory
6from .__pyinstaller import get_hook_dirs
7
8__all__ = ['IconFactory', 'CustomIconFactory']
class IconFactory:
177class IconFactory:
178    """Create an IconFactory for one of the icon sets included with iconipy. All icons created by this 
179    IconFactory will share the same settings, allowing you to change the style for all icons upon 
180    initialization. Providing icon_size as an integer produces a square icon with identical height and 
181    width. Passing a tuple (width, height) enables rectangular dimensions.
182    
183        icon_set (str): The name of the icon set that will be used to create the icon.
184        icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height)
185        font_size (int): The size of the font. Default is icon_size
186        font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
187        outline_width (int): The width of the outline. 0 does not draw an outline
188        outline_color (str, tuple): The color of the outline.  Name or RGBA-Tuple or hex string
189        background_color (str, tuple): The background color. Name or RGBA-Tuple or hex string
190        background_radius (int): The radius of the background corners.
191    """
192       
193    _all_codepoints = {}
194
195    def __init__(
196        self,
197        icon_set: str = "lucide",
198        icon_size: _SizeAttributeType = 64,
199        font_size: int = None,
200        font_color: _ColorAttributeType = "black",
201        outline_width: int = 0,
202        outline_color: _ColorAttributeType = "black",
203        background_color: _ColorAttributeType = None,
204        background_radius: int = 0,
205    ) -> None:
206        if not icon_set in _ICON_SETS.keys():
207            raise ValueError(f'Unknown icon set "{icon_set}"')
208
209        self.icon_set_name = icon_set
210        '''Stores the name of the icon set that is used to create the icons'''
211        
212        self.icon_set_version = self._get_icon_set_version(
213            _ICON_SETS[icon_set]["VERSION_FILE"]
214        )
215        '''Stores the version string for the icon set'''
216
217        try:
218            self._codepoints = IconFactory._all_codepoints[icon_set]
219        except KeyError:
220            IconFactory._all_codepoints = self._read_codepoints()
221            self._codepoints = IconFactory._all_codepoints[icon_set]
222               
223        self.icon_names = list(self._codepoints.keys())
224        '''A list of all icon names for the selected icon set. When the documentation states that *"name" must be a valid key for the codepoints dictionary*, it means the name you enter must be included in this list.'''
225        
226        self.license = self._get_license_text(
227            _ICON_SETS[icon_set]["LICENSE_FILE"]
228        )
229        '''The icon set's license'''        
230        font_size = self._check_font_vs_icon_size(font_size, icon_size)
231        
232        self.icon_sets_available = list(_ICON_SETS.keys())
233        '''A list containing all icon sets that are installed'''
234        
235        self._drawing_kwargs = {
236            "font_path": _ICON_SETS[icon_set]["FONT_FILE"],
237            "icon_size": icon_size,
238            "font_size": font_size,
239            "font_color": font_color,
240            "icon_outline_width": outline_width,
241            "icon_outline_color": outline_color,
242            "icon_background_color": background_color,
243            "icon_background_radius": background_radius,
244        }
245
246        self._temp_dir = TemporaryDirectory()
247    
248    def changeIconSet(self, icon_set: str) -> list:
249        '''Change to a different icon set and retrieve a list containing the icon names
250        of the new set. Available icon sets include: lucide, boxicons, lineicons, material_icons_regular, 
251        material_icons_round_regular, material_icons_sharp_regular, and material_icons_outlined_regular.
252        
253            icon_set (str): The name of the icon set that will be used to create the icons.
254        '''
255        if not icon_set in _ICON_SETS.keys():
256            raise ValueError(f'Unknown icon set "{icon_set}"')
257        self.icon_set_name=icon_set        
258        self._drawing_kwargs['font_path']=_ICON_SETS[icon_set]["FONT_FILE"]
259        self._codepoints = IconFactory._all_codepoints[icon_set]
260        self.icon_names = list(self._codepoints.keys())
261        self.license = self._get_license_text(
262            _ICON_SETS[icon_set]["LICENSE_FILE"]
263        )
264        self.icon_set_version = self._get_icon_set_version(
265            _ICON_SETS[icon_set]["VERSION_FILE"]
266        )
267        return self.icon_names
268    
269    def updateCfg (self,
270                    icon_size: _SizeAttributeType = None,
271                    font_size: int = None,
272                    font_color: _ColorAttributeType = None,
273                    outline_width: int = None,
274                    outline_color: _ColorAttributeType = None,
275                    background_color: _ColorAttributeType = None,
276                    background_radius: int = None,
277                    ) -> dict:
278        '''Modify one or more parameters of the IconFactory object and retrieve a dictionary containing 
279        the updated configuration. Typically, distinct IconFactories are created for different icon 
280        styles, but there may be scenarios where reusing an existing object is desirable.
281        
282            icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height)
283            font_size (int): The size of the font. Default is icon_size
284            font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
285            outline_width (int): The width of the outline. 0 does not draw an outline
286            outline_color (str, tuple): The color of the outline.  Name or RGBA-Tuple or hex string
287            background_color (str, tuple): The background color. Name or RGBA-Tuple or hex string
288            background_radius (int): The radius of the background corners.        
289        '''            
290        if not icon_size == None:
291            if isinstance(icon_size, int):
292                icon_size = icon_size if icon_size>0 else 1
293            elif isinstance(icon_size, tuple): 
294                icon_size = (icon_size[0] if icon_size[0]>0 else 1, icon_size[1] if icon_size[1]>0 else 1)
295            self._drawing_kwargs['icon_size']=icon_size
296        if not font_size ==None:
297            self._drawing_kwargs['font_size']=self._check_font_vs_icon_size(font_size, icon_size)
298        if not font_color == None:
299            self._drawing_kwargs['font_color']=font_color
300        if not outline_width == None:
301            self._drawing_kwargs['icon_outline_width']=outline_width if outline_width>=0 else 0 
302        if not outline_color == None:
303            self._drawing_kwargs['icon_outline_color']=outline_color
304        if not background_color == None:
305            self._drawing_kwargs['icon_background_color']=background_color
306        if not background_radius == None:
307            self._drawing_kwargs['icon_background_radius']=background_radius if background_radius>=0 else 0                                                                     
308        
309        return self._drawing_kwargs
310        
311    def _check_font_vs_icon_size(self, font_size, icon_size):
312        if isinstance(icon_size, int):
313            smallest_side = icon_size
314        elif isinstance(icon_size, tuple) and len(icon_size)==2 and isinstance(icon_size[0], int) and isinstance(icon_size[1], int):
315            smallest_side = icon_size[0] if icon_size[0] <= icon_size[1] else icon_size[1]    
316        else:
317            raise AttributeError('icon_size must be int or tuple (int,int)')     
318         
319        if isinstance(font_size, int) and font_size <= smallest_side:
320            return font_size if font_size >= 0 else 0  
321        else:  
322            return smallest_side if smallest_side > 0 else 1
323                
324    def _read_codepoints(self):
325        codepoints = {}
326        for icon_set in _ICON_SETS.keys():
327            icon_set_codepoints = {}
328            _METADATA_FILE = _ICON_SETS[icon_set]["METADATA_FILE"]
329
330            if icon_set == "lucide":
331                with open(_METADATA_FILE) as json_data:
332                    codepoint_data = json.load(json_data)
333                for key in codepoint_data.keys():
334                    icon_set_codepoints[key] = codepoint_data[key]["encodedCode"][1:]
335
336            elif icon_set in ("boxicons", "microns", "typicons"):
337                pattern = re.compile(
338                    r'.*\.(?P<codepoint_key>([a-z0-9]*-){1,4}[a-z0-9]*):.*\n.*"\\(?P<codepoint_value>.*)";'
339                )
340
341                with open(_METADATA_FILE, "r") as boxicons_codepoint_file:
342                    raw_content = boxicons_codepoint_file.read()
343
344                    # Find all matches in the file content
345                    matches = pattern.finditer(raw_content)
346
347                    # Populate the dictionary with the matches
348                    for match in matches:
349                        codepoint_key = match.group("codepoint_key")
350                        codepoint_value = match.group("codepoint_value")
351                        icon_set_codepoints[codepoint_key] = codepoint_value
352
353            elif icon_set == "ligature_symbols":
354                # Read the HTML content from a file
355                with open(_METADATA_FILE, 'r', encoding='utf-8') as ls_codepoint_file:
356                    html_string = ls_codepoint_file.read()
357
358                pattern = re.compile(
359                    r'<td class="lsf symbol">(.+?)</td>\s*<td class="ligature">.+?</td>\s*<td class="unicode">\\(.+?)</td>'
360                )
361                
362                # Find matches
363                matches = pattern.findall(html_string)
364                
365                # Populate the dictionary with the matches
366                icon_set_codepoints = {match[0]: match[1] for match in matches}              
367                
368            elif icon_set == "lineicons":
369                with open(_METADATA_FILE) as json_data:
370                    codepoint_data = json.load(json_data)
371                for key, value in codepoint_data.items():
372                    icon_set_codepoints[key] = hex(value)[2:]
373
374            else:
375                # Material icons
376                with open(_METADATA_FILE) as material_icons_codepoint_file:
377                    for line in material_icons_codepoint_file:
378                        codepoint_key, codepoint_value = line.strip().split()
379                        icon_set_codepoints[codepoint_key] = codepoint_value
380
381            codepoints[icon_set] = icon_set_codepoints
382
383        return codepoints
384
385    def _get_license_text(self, license_file):
386        with open(license_file, "r") as file_handle:
387            return file_handle.read()
388
389    def _get_icon_set_version(self, version_file):
390        with open(version_file, "r") as file_handle:
391            return file_handle.readline().rstrip()
392
393    def _draw_character(
394        self,
395        character,
396        font_path,
397        font_size=32,
398        font_color="grey",
399        icon_size=32,
400        icon_background_color=None,
401        icon_outline_width=0,
402        icon_background_radius=0,
403        icon_outline_color="dimgrey",
404    ):
405
406        # Create image
407        if isinstance (icon_size, int):
408            width = height = icon_size
409        elif isinstance (icon_size, tuple) and isinstance (icon_size[0], int) and isinstance (icon_size[1], int):
410            width = icon_size[0]
411            height = icon_size[1]
412        else:
413            raise AttributeError ('icon_size must be of type int or tuple (int, int)')
414        
415        # Add a background
416        if icon_background_color or icon_outline_width:
417            image = self._image_round_background(
418                size = (width, height),
419                fill = icon_background_color,
420                outline = icon_outline_color,
421                outline_width = icon_outline_width,
422                outline_radius = icon_background_radius,
423            )
424        else:
425            image = Image.new("RGBA", (width, height), (0, 0, 0, 0))
426
427        if font_size > 0:
428            # Load font
429            font = ImageFont.truetype(font_path, font_size)
430
431            # Draw character
432            draw = ImageDraw.Draw(image)
433
434            x = width // 2
435            y = height // 2
436
437            draw.text((x, y), character, font=font, anchor="mm", fill=font_color)
438        return image
439
440    def _image_round_background(
441        self,
442        size = (64,64),
443        fill = "silver",
444        outline = "grey",
445        outline_width = 7,
446        outline_radius = 10,
447        factor = 3,
448    ):
449        """Create and return a background image with rounded corners. Set the outline radius to size/2 to achieve a circular background."""
450        width = size[0] 
451        height = size[1]
452        im = Image.new("RGBA", (factor * width, factor * height), (0, 0, 0, 0))
453        draw = ImageDraw.Draw(im, "RGBA")
454        draw.rounded_rectangle(
455            (0, 0, (factor * width)-1, (factor * height)-1),
456            radius=factor * outline_radius,
457            outline=outline,
458            fill=fill,
459            width=factor * outline_width,
460        )
461        im = im.resize((width, height), Image.LANCZOS)
462        return im
463
464    def search(self, search_name: str) -> list:
465        """Search for an icon name. Returns a list of icon names containing the search_name"""
466        return [icon_name for icon_name in self.icon_names if search_name.lower() in icon_name.lower()]
467
468    def asPil(self, name: str):
469        """Create image as PIL Image Object, "name" must be a valid key for the codepoints dictionary"""
470        if not name in self._codepoints:
471            raise ValueError(
472                f'Icon with name "{name}" not available. Icon Set: {self.icon_set_name}, Version: {self.icon_set_version}'
473            )
474
475        character = chr(int(self._codepoints[name], 16))
476        return self._draw_character(character, **self._drawing_kwargs)
477
478    def asTkPhotoImage(self, name: str):
479        """Create image as tkinter PhotoImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks), "name" must be a valid key for the codepoints dictionary"""
480        return ImageTk.PhotoImage(self.asPil(name))
481
482    def asTkBitmapImage(self, name: str):
483        """Create image as *monochrome* (two-color) tkinter BitmapImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks),  "name" must be a valid key for the codepoints dictionary"""
484        mode_one_img = self.asPil(name).convert("1")
485        inverted_img = ImageOps.invert(mode_one_img)
486        return ImageTk.BitmapImage(inverted_img)
487           
488    def asBytes(self, name: str, image_format: str="PNG"):
489        """Returns image data as bytestring, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF"""
490        with io.BytesIO() as output:
491            self.asPil(name).save(output, format=image_format)
492            return output.getvalue()
493
494    def asBytesIo(self, name: str, image_format: str="PNG"):
495        """Returns image data as BytesIO object, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF"""
496        output = io.BytesIO()
497        self.asPil(name).save(output, format=image_format)
498        output.seek(0)
499        return output
500
501    def asRawList(self, name: str, type: str="RGB"):
502        """Returns the pixel data of the image as a list. "name" must be a valid key for the codepoints dictionary, type="RGB" contains values 0-255, type="FLOAT" contains values 0-1"""
503
504        def _calc_pixel_value(value, type):
505            if type == "FLOAT":
506                return value / 255.0
507            return value  # 'RGB' or any other type
508
509        icon = self.asPil(name)
510        pixel_data = []
511        # Process image to list
512        # numpy etc. are WAY faster but introduce new dependencies
513        for i in range(0, icon.height):
514            for j in range(0, icon.width):
515                pixel = icon.getpixel((j, i))
516                pixel_data.append(_calc_pixel_value(pixel[0], type))
517                pixel_data.append(_calc_pixel_value(pixel[1], type))
518                pixel_data.append(_calc_pixel_value(pixel[2], type))
519                pixel_data.append(_calc_pixel_value(pixel[3], type))
520        return pixel_data
521
522    def asQImage(self, name: str):
523        """Create image as QImage Object, "name" must be a valid key for the codepoints dictionary"""
524        return ImageQt.ImageQt(self.asPil(name))
525
526    def asQPixmap(self, name: str):
527        """Create image as QPixmap Object, "name" must be a valid key for the codepoints dictionary"""
528        return ImageQt.toqpixmap(self.asPil(name))
529    
530    def asTempFile(self, name: str, extension: str="png"):
531        '''Returns a path to a temporary image file.  If your framework only accepts file paths, you can use this function. The image format is determined by the file extension (Default is "png") and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary.'''
532        filepath = os.path.join(self._temp_dir.name, f'{str(uuid.uuid4())}.{extension.lower()}')
533        self.save(name, filepath)   
534        return filepath
535    
536    def save(self, name: str, save_as: str):
537        """Saves the icon to file "save_as", the image format is determined by the file extension and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary"""
538        kwargs={}
539        if save_as.lower().endswith('.ico'):
540            kwargs['sizes'] = [self._drawing_kwargs['icon_size']]
541        self.asPil(name).save(save_as, **kwargs)
542
543    def saveAll(self, save_to_dir: str, extension: str="png"):
544        '''Saves all icons in the icon set to path "save_to_dir", the image format is determined by the "extension" and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported.'''
545        for name in self._codepoints.keys():
546            self.save(name, os.path.join(save_to_dir, f"{name}.{extension.lower()}"))
547
548    def show(self, name: str):
549        """Show the icon in an external viewer using the PIL Image.show() method. "name" must be a valid key for the codepoints dictionary"""
550        self.asPil(name).show()

Create an IconFactory for one of the icon sets included with iconipy. All icons created by this IconFactory will share the same settings, allowing you to change the style for all icons upon initialization. Providing icon_size as an integer produces a square icon with identical height and width. Passing a tuple (width, height) enables rectangular dimensions.

icon_set (str): The name of the icon set that will be used to create the icon.
icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height)
font_size (int): The size of the font. Default is icon_size
font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
outline_width (int): The width of the outline. 0 does not draw an outline
outline_color (str, tuple): The color of the outline.  Name or RGBA-Tuple or hex string
background_color (str, tuple): The background color. Name or RGBA-Tuple or hex string
background_radius (int): The radius of the background corners.
IconFactory( icon_set: str = 'lucide', icon_size: Union[Tuple, int] = 64, font_size: int = None, font_color: Union[Tuple, str] = 'black', outline_width: int = 0, outline_color: Union[Tuple, str] = 'black', background_color: Union[Tuple, str] = None, background_radius: int = 0)
195    def __init__(
196        self,
197        icon_set: str = "lucide",
198        icon_size: _SizeAttributeType = 64,
199        font_size: int = None,
200        font_color: _ColorAttributeType = "black",
201        outline_width: int = 0,
202        outline_color: _ColorAttributeType = "black",
203        background_color: _ColorAttributeType = None,
204        background_radius: int = 0,
205    ) -> None:
206        if not icon_set in _ICON_SETS.keys():
207            raise ValueError(f'Unknown icon set "{icon_set}"')
208
209        self.icon_set_name = icon_set
210        '''Stores the name of the icon set that is used to create the icons'''
211        
212        self.icon_set_version = self._get_icon_set_version(
213            _ICON_SETS[icon_set]["VERSION_FILE"]
214        )
215        '''Stores the version string for the icon set'''
216
217        try:
218            self._codepoints = IconFactory._all_codepoints[icon_set]
219        except KeyError:
220            IconFactory._all_codepoints = self._read_codepoints()
221            self._codepoints = IconFactory._all_codepoints[icon_set]
222               
223        self.icon_names = list(self._codepoints.keys())
224        '''A list of all icon names for the selected icon set. When the documentation states that *"name" must be a valid key for the codepoints dictionary*, it means the name you enter must be included in this list.'''
225        
226        self.license = self._get_license_text(
227            _ICON_SETS[icon_set]["LICENSE_FILE"]
228        )
229        '''The icon set's license'''        
230        font_size = self._check_font_vs_icon_size(font_size, icon_size)
231        
232        self.icon_sets_available = list(_ICON_SETS.keys())
233        '''A list containing all icon sets that are installed'''
234        
235        self._drawing_kwargs = {
236            "font_path": _ICON_SETS[icon_set]["FONT_FILE"],
237            "icon_size": icon_size,
238            "font_size": font_size,
239            "font_color": font_color,
240            "icon_outline_width": outline_width,
241            "icon_outline_color": outline_color,
242            "icon_background_color": background_color,
243            "icon_background_radius": background_radius,
244        }
245
246        self._temp_dir = TemporaryDirectory()
icon_set_name

Stores the name of the icon set that is used to create the icons

icon_set_version

Stores the version string for the icon set

icon_names

A list of all icon names for the selected icon set. When the documentation states that "name" must be a valid key for the codepoints dictionary, it means the name you enter must be included in this list.

license

The icon set's license

icon_sets_available

A list containing all icon sets that are installed

def changeIconSet(self, icon_set: str) -> list:
248    def changeIconSet(self, icon_set: str) -> list:
249        '''Change to a different icon set and retrieve a list containing the icon names
250        of the new set. Available icon sets include: lucide, boxicons, lineicons, material_icons_regular, 
251        material_icons_round_regular, material_icons_sharp_regular, and material_icons_outlined_regular.
252        
253            icon_set (str): The name of the icon set that will be used to create the icons.
254        '''
255        if not icon_set in _ICON_SETS.keys():
256            raise ValueError(f'Unknown icon set "{icon_set}"')
257        self.icon_set_name=icon_set        
258        self._drawing_kwargs['font_path']=_ICON_SETS[icon_set]["FONT_FILE"]
259        self._codepoints = IconFactory._all_codepoints[icon_set]
260        self.icon_names = list(self._codepoints.keys())
261        self.license = self._get_license_text(
262            _ICON_SETS[icon_set]["LICENSE_FILE"]
263        )
264        self.icon_set_version = self._get_icon_set_version(
265            _ICON_SETS[icon_set]["VERSION_FILE"]
266        )
267        return self.icon_names

Change to a different icon set and retrieve a list containing the icon names of the new set. Available icon sets include: lucide, boxicons, lineicons, material_icons_regular, material_icons_round_regular, material_icons_sharp_regular, and material_icons_outlined_regular.

icon_set (str): The name of the icon set that will be used to create the icons.
def updateCfg( self, icon_size: Union[Tuple, int] = None, font_size: int = None, font_color: Union[Tuple, str] = None, outline_width: int = None, outline_color: Union[Tuple, str] = None, background_color: Union[Tuple, str] = None, background_radius: int = None) -> dict:
269    def updateCfg (self,
270                    icon_size: _SizeAttributeType = None,
271                    font_size: int = None,
272                    font_color: _ColorAttributeType = None,
273                    outline_width: int = None,
274                    outline_color: _ColorAttributeType = None,
275                    background_color: _ColorAttributeType = None,
276                    background_radius: int = None,
277                    ) -> dict:
278        '''Modify one or more parameters of the IconFactory object and retrieve a dictionary containing 
279        the updated configuration. Typically, distinct IconFactories are created for different icon 
280        styles, but there may be scenarios where reusing an existing object is desirable.
281        
282            icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height)
283            font_size (int): The size of the font. Default is icon_size
284            font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
285            outline_width (int): The width of the outline. 0 does not draw an outline
286            outline_color (str, tuple): The color of the outline.  Name or RGBA-Tuple or hex string
287            background_color (str, tuple): The background color. Name or RGBA-Tuple or hex string
288            background_radius (int): The radius of the background corners.        
289        '''            
290        if not icon_size == None:
291            if isinstance(icon_size, int):
292                icon_size = icon_size if icon_size>0 else 1
293            elif isinstance(icon_size, tuple): 
294                icon_size = (icon_size[0] if icon_size[0]>0 else 1, icon_size[1] if icon_size[1]>0 else 1)
295            self._drawing_kwargs['icon_size']=icon_size
296        if not font_size ==None:
297            self._drawing_kwargs['font_size']=self._check_font_vs_icon_size(font_size, icon_size)
298        if not font_color == None:
299            self._drawing_kwargs['font_color']=font_color
300        if not outline_width == None:
301            self._drawing_kwargs['icon_outline_width']=outline_width if outline_width>=0 else 0 
302        if not outline_color == None:
303            self._drawing_kwargs['icon_outline_color']=outline_color
304        if not background_color == None:
305            self._drawing_kwargs['icon_background_color']=background_color
306        if not background_radius == None:
307            self._drawing_kwargs['icon_background_radius']=background_radius if background_radius>=0 else 0                                                                     
308        
309        return self._drawing_kwargs

Modify one or more parameters of the IconFactory object and retrieve a dictionary containing the updated configuration. Typically, distinct IconFactories are created for different icon styles, but there may be scenarios where reusing an existing object is desirable.

icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height)
font_size (int): The size of the font. Default is icon_size
font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
outline_width (int): The width of the outline. 0 does not draw an outline
outline_color (str, tuple): The color of the outline.  Name or RGBA-Tuple or hex string
background_color (str, tuple): The background color. Name or RGBA-Tuple or hex string
background_radius (int): The radius of the background corners.
def search(self, search_name: str) -> list:
464    def search(self, search_name: str) -> list:
465        """Search for an icon name. Returns a list of icon names containing the search_name"""
466        return [icon_name for icon_name in self.icon_names if search_name.lower() in icon_name.lower()]

Search for an icon name. Returns a list of icon names containing the search_name

def asPil(self, name: str):
468    def asPil(self, name: str):
469        """Create image as PIL Image Object, "name" must be a valid key for the codepoints dictionary"""
470        if not name in self._codepoints:
471            raise ValueError(
472                f'Icon with name "{name}" not available. Icon Set: {self.icon_set_name}, Version: {self.icon_set_version}'
473            )
474
475        character = chr(int(self._codepoints[name], 16))
476        return self._draw_character(character, **self._drawing_kwargs)

Create image as PIL Image Object, "name" must be a valid key for the codepoints dictionary

def asTkPhotoImage(self, name: str):
478    def asTkPhotoImage(self, name: str):
479        """Create image as tkinter PhotoImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks), "name" must be a valid key for the codepoints dictionary"""
480        return ImageTk.PhotoImage(self.asPil(name))

Create image as tkinter PhotoImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks), "name" must be a valid key for the codepoints dictionary

def asTkBitmapImage(self, name: str):
482    def asTkBitmapImage(self, name: str):
483        """Create image as *monochrome* (two-color) tkinter BitmapImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks),  "name" must be a valid key for the codepoints dictionary"""
484        mode_one_img = self.asPil(name).convert("1")
485        inverted_img = ImageOps.invert(mode_one_img)
486        return ImageTk.BitmapImage(inverted_img)

Create image as monochrome (two-color) tkinter BitmapImage Object. Make sure you initialize tkinter first. Place your function call after creating the root instance (root = Tk() or equivalent for other GUI frameworks), "name" must be a valid key for the codepoints dictionary

def asBytes(self, name: str, image_format: str = 'PNG'):
488    def asBytes(self, name: str, image_format: str="PNG"):
489        """Returns image data as bytestring, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF"""
490        with io.BytesIO() as output:
491            self.asPil(name).save(output, format=image_format)
492            return output.getvalue()

Returns image data as bytestring, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF

def asBytesIo(self, name: str, image_format: str = 'PNG'):
494    def asBytesIo(self, name: str, image_format: str="PNG"):
495        """Returns image data as BytesIO object, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF"""
496        output = io.BytesIO()
497        self.asPil(name).save(output, format=image_format)
498        output.seek(0)
499        return output

Returns image data as BytesIO object, "name" must be a valid key for the codepoints dictionary, the image_format parameter should be set to one of the formats supported by Pillow. These formats include common image types like JPEG, PNG, ICO, and GIF

def asRawList(self, name: str, type: str = 'RGB'):
501    def asRawList(self, name: str, type: str="RGB"):
502        """Returns the pixel data of the image as a list. "name" must be a valid key for the codepoints dictionary, type="RGB" contains values 0-255, type="FLOAT" contains values 0-1"""
503
504        def _calc_pixel_value(value, type):
505            if type == "FLOAT":
506                return value / 255.0
507            return value  # 'RGB' or any other type
508
509        icon = self.asPil(name)
510        pixel_data = []
511        # Process image to list
512        # numpy etc. are WAY faster but introduce new dependencies
513        for i in range(0, icon.height):
514            for j in range(0, icon.width):
515                pixel = icon.getpixel((j, i))
516                pixel_data.append(_calc_pixel_value(pixel[0], type))
517                pixel_data.append(_calc_pixel_value(pixel[1], type))
518                pixel_data.append(_calc_pixel_value(pixel[2], type))
519                pixel_data.append(_calc_pixel_value(pixel[3], type))
520        return pixel_data

Returns the pixel data of the image as a list. "name" must be a valid key for the codepoints dictionary, type="RGB" contains values 0-255, type="FLOAT" contains values 0-1

def asQImage(self, name: str):
522    def asQImage(self, name: str):
523        """Create image as QImage Object, "name" must be a valid key for the codepoints dictionary"""
524        return ImageQt.ImageQt(self.asPil(name))

Create image as QImage Object, "name" must be a valid key for the codepoints dictionary

def asQPixmap(self, name: str):
526    def asQPixmap(self, name: str):
527        """Create image as QPixmap Object, "name" must be a valid key for the codepoints dictionary"""
528        return ImageQt.toqpixmap(self.asPil(name))

Create image as QPixmap Object, "name" must be a valid key for the codepoints dictionary

def asTempFile(self, name: str, extension: str = 'png'):
530    def asTempFile(self, name: str, extension: str="png"):
531        '''Returns a path to a temporary image file.  If your framework only accepts file paths, you can use this function. The image format is determined by the file extension (Default is "png") and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary.'''
532        filepath = os.path.join(self._temp_dir.name, f'{str(uuid.uuid4())}.{extension.lower()}')
533        self.save(name, filepath)   
534        return filepath

Returns a path to a temporary image file. If your framework only accepts file paths, you can use this function. The image format is determined by the file extension (Default is "png") and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary.

def save(self, name: str, save_as: str):
536    def save(self, name: str, save_as: str):
537        """Saves the icon to file "save_as", the image format is determined by the file extension and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary"""
538        kwargs={}
539        if save_as.lower().endswith('.ico'):
540            kwargs['sizes'] = [self._drawing_kwargs['icon_size']]
541        self.asPil(name).save(save_as, **kwargs)

Saves the icon to file "save_as", the image format is determined by the file extension and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported. "name" must be a valid key for the codepoints dictionary

def saveAll(self, save_to_dir: str, extension: str = 'png'):
543    def saveAll(self, save_to_dir: str, extension: str="png"):
544        '''Saves all icons in the icon set to path "save_to_dir", the image format is determined by the "extension" and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported.'''
545        for name in self._codepoints.keys():
546            self.save(name, os.path.join(save_to_dir, f"{name}.{extension.lower()}"))

Saves all icons in the icon set to path "save_to_dir", the image format is determined by the "extension" and should be set to one of the formats supported by Pillow. Only formats that support transparency (ico, png, gif, webp, jp2, ...) are supported.

def show(self, name: str):
548    def show(self, name: str):
549        """Show the icon in an external viewer using the PIL Image.show() method. "name" must be a valid key for the codepoints dictionary"""
550        self.asPil(name).show()

Show the icon in an external viewer using the PIL Image.show() method. "name" must be a valid key for the codepoints dictionary

class CustomIconFactory(iconipy.IconFactory):
553class CustomIconFactory(IconFactory):
554    """Create an IconFactory for a custom icon set by providing a font path
555    (supported by the FreeType library, e.g., TrueType, OpenType) and a
556    dictionary of codepoints. The dictionary keys should be the icon names
557    ('microphone'), and the values should be the corresponding
558    hexadecimal codepoints ('E02A'). Providing icon_size as an integer produces 
559    a square icon with identical height and width. Passing a tuple (width, height) 
560    enables rectangular dimensions.
561    
562        icon_set (str): The name of the icon set that will be used to create the icon.
563        icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height) 
564        font_size (int): The size of the font. Default is icon_size
565        font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
566        outline_width (int): The width of the outline. 0 does not draw an outline
567        outline_color (str, tuple): The color of the outline.  Name, RGBA-Tuple or hex string
568        background_color (str, tuple): The background color. Name, RGBA-Tuple or hex string
569        background_radius (int): The radius of the background corners.
570        font_path (str): The path to the custom icon set font file.
571        codepoints (dict): A dictionary of icon names and codepoints.
572        version (str): The version of the icon set.
573    """
574
575    def __init__(
576        self,
577        icon_set: str = "custom",
578        icon_size: _SizeAttributeType = 64,
579        font_size: int = None,
580        font_color: _ColorAttributeType = "black",
581        outline_width: int = 0,
582        outline_color: _ColorAttributeType = "black",
583        background_color: _ColorAttributeType = None,
584        background_radius: int = 0,
585        font_path: str = None,
586        codepoints: dict = None,
587        version: str = "0.1",
588    ) -> None:
589        if not font_path or not codepoints:
590            raise ValueError(
591                f'You need to supply a font path and codepoint dictionary"'
592            )
593
594        self.icon_set_name = icon_set
595        '''Stores the name of the icon set'''
596
597        self.icon_set_version = version
598        '''Stores the version string for the icon set'''
599        
600        self._codepoints = codepoints
601
602        self.icon_names = list(self._codepoints.keys())
603        '''A list of all icon names for the selected icon set. When the documentation states that *"name" must be a valid key for the codepoints dictionary*, it means the name you enter must be included in this list.'''
604
605        self.license = 'Unknown License'
606        '''For custom icon sets the default value is "Unknown License"'''        
607        
608        font_size = self._check_font_vs_icon_size(font_size, icon_size)
609
610        self._drawing_kwargs = {
611            "font_path": font_path,
612            "font_size": font_size,
613            "font_color": font_color,
614            "icon_size": icon_size,
615            "icon_background_color": background_color,
616            "icon_outline_width": outline_width,
617            "icon_background_radius": background_radius,
618            "icon_outline_color": outline_color,
619        }
620
621    def changeIconSet(self, icon_set: str):
622        '''Not implemented for CustomIconFactory'''
623        raise NotImplementedError('changeIconSet is not implemented for CustomIconFactory') 

Create an IconFactory for a custom icon set by providing a font path (supported by the FreeType library, e.g., TrueType, OpenType) and a dictionary of codepoints. The dictionary keys should be the icon names ('microphone'), and the values should be the corresponding hexadecimal codepoints ('E02A'). Providing icon_size as an integer produces a square icon with identical height and width. Passing a tuple (width, height) enables rectangular dimensions.

icon_set (str): The name of the icon set that will be used to create the icon.
icon_size (int, tuple): The dimensions of the icons in pixels. Single int value or tuple (width, height) 
font_size (int): The size of the font. Default is icon_size
font_color (str, tuple): The color of the font. Name, RGBA-Tuple or hex string
outline_width (int): The width of the outline. 0 does not draw an outline
outline_color (str, tuple): The color of the outline.  Name, RGBA-Tuple or hex string
background_color (str, tuple): The background color. Name, RGBA-Tuple or hex string
background_radius (int): The radius of the background corners.
font_path (str): The path to the custom icon set font file.
codepoints (dict): A dictionary of icon names and codepoints.
version (str): The version of the icon set.
CustomIconFactory( icon_set: str = 'custom', icon_size: Union[Tuple, int] = 64, font_size: int = None, font_color: Union[Tuple, str] = 'black', outline_width: int = 0, outline_color: Union[Tuple, str] = 'black', background_color: Union[Tuple, str] = None, background_radius: int = 0, font_path: str = None, codepoints: dict = None, version: str = '0.1')
575    def __init__(
576        self,
577        icon_set: str = "custom",
578        icon_size: _SizeAttributeType = 64,
579        font_size: int = None,
580        font_color: _ColorAttributeType = "black",
581        outline_width: int = 0,
582        outline_color: _ColorAttributeType = "black",
583        background_color: _ColorAttributeType = None,
584        background_radius: int = 0,
585        font_path: str = None,
586        codepoints: dict = None,
587        version: str = "0.1",
588    ) -> None:
589        if not font_path or not codepoints:
590            raise ValueError(
591                f'You need to supply a font path and codepoint dictionary"'
592            )
593
594        self.icon_set_name = icon_set
595        '''Stores the name of the icon set'''
596
597        self.icon_set_version = version
598        '''Stores the version string for the icon set'''
599        
600        self._codepoints = codepoints
601
602        self.icon_names = list(self._codepoints.keys())
603        '''A list of all icon names for the selected icon set. When the documentation states that *"name" must be a valid key for the codepoints dictionary*, it means the name you enter must be included in this list.'''
604
605        self.license = 'Unknown License'
606        '''For custom icon sets the default value is "Unknown License"'''        
607        
608        font_size = self._check_font_vs_icon_size(font_size, icon_size)
609
610        self._drawing_kwargs = {
611            "font_path": font_path,
612            "font_size": font_size,
613            "font_color": font_color,
614            "icon_size": icon_size,
615            "icon_background_color": background_color,
616            "icon_outline_width": outline_width,
617            "icon_background_radius": background_radius,
618            "icon_outline_color": outline_color,
619        }
icon_set_name

Stores the name of the icon set

icon_set_version

Stores the version string for the icon set

icon_names

A list of all icon names for the selected icon set. When the documentation states that "name" must be a valid key for the codepoints dictionary, it means the name you enter must be included in this list.

license

For custom icon sets the default value is "Unknown License"

def changeIconSet(self, icon_set: str):
621    def changeIconSet(self, icon_set: str):
622        '''Not implemented for CustomIconFactory'''
623        raise NotImplementedError('changeIconSet is not implemented for CustomIconFactory') 

Not implemented for CustomIconFactory