Custom QIconEngine that automatically handles DPI scaling for tools icons. This engine provides icons on-demand with proper scaling based on the current device pixel ratio, eliminating the need for manual refresh when DPI changes.
| 684 | |
| 685 | |
| 686 | class _IconEngine(QtGui.QIconEngine): |
| 687 | """ |
| 688 | Custom QIconEngine that automatically handles DPI scaling for tools icons. |
| 689 | |
| 690 | This engine provides icons on-demand with proper scaling based on the current |
| 691 | device pixel ratio, eliminating the need for manual refresh when DPI changes. |
| 692 | """ |
| 693 | |
| 694 | def __init__(self, image_path, toolbar=None): |
| 695 | super().__init__() |
| 696 | self.image_path = image_path |
| 697 | self.toolbar = toolbar |
| 698 | |
| 699 | def _is_dark_mode(self): |
| 700 | return self.toolbar.palette().color(self.toolbar.backgroundRole()).value() < 128 |
| 701 | |
| 702 | def paint(self, painter, rect, mode, state): |
| 703 | """Paint the icon at the requested size and state.""" |
| 704 | pixmap = self.pixmap(rect.size(), mode, state) |
| 705 | if not pixmap.isNull(): |
| 706 | painter.drawPixmap(rect, pixmap) |
| 707 | |
| 708 | def pixmap(self, size, mode, state): |
| 709 | """Generate a pixmap for the requested size, mode, and state.""" |
| 710 | if size.width() <= 0 or size.height() <= 0: |
| 711 | return QtGui.QPixmap() |
| 712 | |
| 713 | # Try SVG first, then fall back to PNG |
| 714 | svg_path = self.image_path.with_suffix('.svg') |
| 715 | if svg_path.exists(): |
| 716 | pixmap = self._create_pixmap_from_svg(svg_path, size) |
| 717 | if not pixmap.isNull(): |
| 718 | return pixmap |
| 719 | return self._create_pixmap_from_png(self.image_path, size) |
| 720 | |
| 721 | def _devicePixelRatio(self): |
| 722 | """Return the current device pixel ratio for the toolbar, defaulting to 1.""" |
| 723 | return (self.toolbar.devicePixelRatioF() or 1) if self.toolbar else 1 |
| 724 | |
| 725 | def _create_pixmap_from_svg(self, svg_path, size): |
| 726 | """Create a pixmap from SVG with proper scaling and dark mode support.""" |
| 727 | QSvgRenderer = getattr(QtSvg, "QSvgRenderer", None) |
| 728 | if QSvgRenderer is None: |
| 729 | return QtGui.QPixmap() |
| 730 | |
| 731 | svg_content = svg_path.read_bytes() |
| 732 | |
| 733 | if self._is_dark_mode(): |
| 734 | svg_content = svg_content.replace(b'fill:black;', b'fill:white;') |
| 735 | svg_content = svg_content.replace(b'stroke:black;', b'stroke:white;') |
| 736 | |
| 737 | renderer = QSvgRenderer(QtCore.QByteArray(svg_content)) |
| 738 | if not renderer.isValid(): |
| 739 | return QtGui.QPixmap() |
| 740 | |
| 741 | dpr = self._devicePixelRatio() |
| 742 | scaled_size = QtCore.QSize(int(size.width() * dpr), int(size.height() * dpr)) |
| 743 | pixmap = QtGui.QPixmap(scaled_size) |
no outgoing calls
no test coverage detected
searching dependent graphs…