How to create a QIcon from a .exe path?
-
Hello, I'm trying to add installed applications and their icons to a QComboBox.
Below is an example code that anyone can copy and run.
It successfully retrieves installed applications from the registry, and also the path of their icon if it exists.
The problem is that QIcon is only created if the application's icon_path is a path with a .ico extension, so if a program's icon_path is retrieved as .exe, a QIcon won't get created.
You can run the code and compare what paths it prints out to the QIcons that are created in the QComboBox:
import sys import os import winreg from PyQt6.QtWidgets import QApplication, QComboBox, QMainWindow from PyQt6.QtGui import QIcon from PyQt6.QtCore import QSize def get_installed_apps_from_registry(registry_path): apps = [] try: reg_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_READ) for i in range(0, winreg.QueryInfoKey(reg_key)[0]): try: subkey_name = winreg.EnumKey(reg_key, i) subkey = winreg.OpenKey(reg_key, subkey_name) # Retrieve app name app_name, _ = winreg.QueryValueEx(subkey, "DisplayName") # Retrieve app icon path try: icon_path, _ = winreg.QueryValueEx(subkey, "DisplayIcon") icon_path = icon_path.split(",")[0].strip() # Clean up icon path if not os.path.isfile(icon_path): icon_path = None except FileNotFoundError: icon_path = None # Append app name and icon path to the list apps.append({"name": app_name, "icon": icon_path}) except EnvironmentError: continue finally: subkey.Close() except EnvironmentError: pass return apps def list_installed_apps(): registry_paths = [ r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ] installed_apps = [] for path in registry_paths: installed_apps.extend(get_installed_apps_from_registry(path)) # Remove duplicates and sort unique_apps = {app['name']: app for app in installed_apps if app['name']} sorted_apps = sorted(unique_apps.values(), key=lambda x: x['name']) return sorted_apps def create_icon(icon_path): # Create QIcon from path if icon_path and os.path.isfile(icon_path): return QIcon(icon_path) return QIcon() # Return empty icon if path is invalid class AppComboBox(QMainWindow): def __init__(self, apps): super().__init__() self.setWindowTitle("Installed Applications") self.setGeometry(100, 100, 400, 100) # Create QComboBox self.combo = QComboBox(self) self.combo.setIconSize(QSize(32, 32)) self.combo.setGeometry(50, 20, 300, 40) # Add apps to combo box with icons for app in apps: icon = create_icon(app['icon']) self.combo.addItem(icon, app['name']) def main(): # Fetch installed applications apps = list_installed_apps() # Print program name and icon path for app in apps: print(f"Name: {app['name']}") print(f"Icon Path: {app['icon']}") print("-" * 40) # Set up the PyQt6 application app = QApplication(sys.argv) window = AppComboBox(apps) window.show() sys.exit(app.exec()) if __name__ == "__main__": main()
-
Hi,
That's because QIcon has no reason to know how to extract a file from an exe binary.
It's a step you have to implement yourself. -
Hello, I'm trying to add installed applications and their icons to a QComboBox.
Below is an example code that anyone can copy and run.
It successfully retrieves installed applications from the registry, and also the path of their icon if it exists.
The problem is that QIcon is only created if the application's icon_path is a path with a .ico extension, so if a program's icon_path is retrieved as .exe, a QIcon won't get created.
You can run the code and compare what paths it prints out to the QIcons that are created in the QComboBox:
import sys import os import winreg from PyQt6.QtWidgets import QApplication, QComboBox, QMainWindow from PyQt6.QtGui import QIcon from PyQt6.QtCore import QSize def get_installed_apps_from_registry(registry_path): apps = [] try: reg_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_READ) for i in range(0, winreg.QueryInfoKey(reg_key)[0]): try: subkey_name = winreg.EnumKey(reg_key, i) subkey = winreg.OpenKey(reg_key, subkey_name) # Retrieve app name app_name, _ = winreg.QueryValueEx(subkey, "DisplayName") # Retrieve app icon path try: icon_path, _ = winreg.QueryValueEx(subkey, "DisplayIcon") icon_path = icon_path.split(",")[0].strip() # Clean up icon path if not os.path.isfile(icon_path): icon_path = None except FileNotFoundError: icon_path = None # Append app name and icon path to the list apps.append({"name": app_name, "icon": icon_path}) except EnvironmentError: continue finally: subkey.Close() except EnvironmentError: pass return apps def list_installed_apps(): registry_paths = [ r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ] installed_apps = [] for path in registry_paths: installed_apps.extend(get_installed_apps_from_registry(path)) # Remove duplicates and sort unique_apps = {app['name']: app for app in installed_apps if app['name']} sorted_apps = sorted(unique_apps.values(), key=lambda x: x['name']) return sorted_apps def create_icon(icon_path): # Create QIcon from path if icon_path and os.path.isfile(icon_path): return QIcon(icon_path) return QIcon() # Return empty icon if path is invalid class AppComboBox(QMainWindow): def __init__(self, apps): super().__init__() self.setWindowTitle("Installed Applications") self.setGeometry(100, 100, 400, 100) # Create QComboBox self.combo = QComboBox(self) self.combo.setIconSize(QSize(32, 32)) self.combo.setGeometry(50, 20, 300, 40) # Add apps to combo box with icons for app in apps: icon = create_icon(app['icon']) self.combo.addItem(icon, app['name']) def main(): # Fetch installed applications apps = list_installed_apps() # Print program name and icon path for app in apps: print(f"Name: {app['name']}") print(f"Icon Path: {app['icon']}") print("-" * 40) # Set up the PyQt6 application app = QApplication(sys.argv) window = AppComboBox(apps) window.show() sys.exit(app.exec()) if __name__ == "__main__": main()
@Mizmas
I think https://stackoverflow.com/questions/1616342/best-way-to-extract-ico-from-exe-and-paint-with-pyqt shows how you have to do this. Read each answer carefully, and the little comments, to allow for PyQt6 (or PySide6).If you can translate the few lines of https://stackoverflow.com/a/50809652/489865 into PyQt/PySide and it works on an exe that's even simpler.
-
@Mizmas
I think https://stackoverflow.com/questions/1616342/best-way-to-extract-ico-from-exe-and-paint-with-pyqt shows how you have to do this. Read each answer carefully, and the little comments, to allow for PyQt6 (or PySide6).If you can translate the few lines of https://stackoverflow.com/a/50809652/489865 into PyQt/PySide and it works on an exe that's even simpler.
@JonB Thanks a lot. Apparently the QFileIconProvider class was removed from PyQt6, but QFileSystemModel can be used instead.
Here's the updated code, it now successfully creates a QIcon for .exe icon paths. On my system there's still some application icon paths that aren't being found by the get_installed_apps_from_registry method, or maybe they just don't exist in the registry, so maybe someone will be able to improve upon this code:
import sys import os import winreg from PyQt6.QtWidgets import QApplication, QComboBox, QMainWindow from PyQt6.QtGui import QIcon, QFileSystemModel from PyQt6.QtCore import QSize def get_installed_apps_from_registry(registry_path): apps = [] try: reg_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, registry_path, 0, winreg.KEY_READ) for i in range(0, winreg.QueryInfoKey(reg_key)[0]): try: subkey_name = winreg.EnumKey(reg_key, i) subkey = winreg.OpenKey(reg_key, subkey_name) # Retrieve app name app_name, _ = winreg.QueryValueEx(subkey, "DisplayName") # Retrieve app icon path try: icon_path, _ = winreg.QueryValueEx(subkey, "DisplayIcon") icon_path = icon_path.split(",")[0].strip() # Clean up icon path if not os.path.isfile(icon_path): icon_path = None except FileNotFoundError: icon_path = None # Append app name and icon path to the list apps.append({"name": app_name, "icon": icon_path}) except EnvironmentError: continue finally: subkey.Close() except EnvironmentError: pass return apps def list_installed_apps(): registry_paths = [ r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ] installed_apps = [] for path in registry_paths: installed_apps.extend(get_installed_apps_from_registry(path)) # Remove duplicates and sort unique_apps = {app['name']: app for app in installed_apps if app['name']} sorted_apps = sorted(unique_apps.values(), key=lambda x: x['name']) return sorted_apps class AppComboBox(QMainWindow): def __init__(self, apps): super().__init__() self.setWindowTitle("Installed Applications") self.setGeometry(100, 100, 400, 100) # Create QComboBox self.combo = QComboBox(self) self.combo.setIconSize(QSize(32, 32)) self.combo.setGeometry(50, 20, 300, 40) # Initialize QFileSystemModel to fetch icons self.file_model = QFileSystemModel() # Add apps to combo box with icons for app in apps: icon = self.get_icon(app['icon']) if app['icon'] else QIcon() self.combo.addItem(icon, app['name']) def get_icon(self, icon_path): # Fetches an icon using QFileSystemModel based on file path if icon_path and os.path.isfile(icon_path): index = self.file_model.index(icon_path) return self.file_model.fileIcon(index) return QIcon() # Return an empty icon if path is invalid def main(): # Fetch installed applications apps = list_installed_apps() # Print program name and icon path for app in apps: print(f"Name: {app['name']}") print(f"Icon Path: {app['icon']}") print("-" * 40) # Set up the PyQt6 application app = QApplication(sys.argv) window = AppComboBox(apps) window.show() sys.exit(app.exec()) if __name__ == "__main__": main()
-