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.
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.
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()
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.
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.
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.
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
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
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
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
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
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
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
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
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
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.
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
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.
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
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.
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 }