|
28 | 28 | QPointF, |
29 | 29 | QRect, |
30 | 30 | QRectF, |
| 31 | + QSize, |
31 | 32 | Qt, |
32 | 33 | qFuzzyCompare, |
33 | 34 | ) |
34 | | -from qtpy.QtGui import QFont, QFontMetrics, QPalette, QTransform |
| 35 | +from qtpy.QtGui import QFont, QFontMetrics, QPainter, QPalette, QPixmap, QTransform |
35 | 36 |
|
36 | 37 | from qwt._math import qwtRadians |
37 | 38 | from qwt.scale_div import QwtScaleDiv |
@@ -1041,10 +1042,117 @@ def drawLabel(self, painter, value): |
1041 | 1042 | if lbl is None or lbl.isEmpty(): |
1042 | 1043 | return |
1043 | 1044 | pos = self._labelPositionWithFont(painter.font(), value) |
1044 | | - transform = self.labelTransformation(pos, labelSize) |
| 1045 | + |
| 1046 | + # For rotated text, choose rendering method based on rotation angle |
| 1047 | + rotation = self.labelRotation() |
| 1048 | + if abs(rotation) > 1e-6: |
| 1049 | + # Check if rotation is a multiple of 90 degrees (within tolerance) |
| 1050 | + normalized_rotation = rotation % 360 |
| 1051 | + is_90_degree_multiple = ( |
| 1052 | + abs(normalized_rotation) < 1e-6 or |
| 1053 | + abs(normalized_rotation - 90) < 1e-6 or |
| 1054 | + abs(normalized_rotation - 180) < 1e-6 or |
| 1055 | + abs(normalized_rotation - 270) < 1e-6 or |
| 1056 | + abs(normalized_rotation - 360) < 1e-6 |
| 1057 | + ) |
| 1058 | + |
| 1059 | + if is_90_degree_multiple: |
| 1060 | + # Use direct rendering for 90-degree multiples (crisp) |
| 1061 | + transform = self.labelTransformation(pos, labelSize) |
| 1062 | + painter.save() |
| 1063 | + painter.setRenderHint(QPainter.TextAntialiasing, True) |
| 1064 | + painter.setWorldTransform(transform, True) |
| 1065 | + lbl.draw(painter, QRect(QPoint(0, 0), labelSize.toSize())) |
| 1066 | + painter.restore() |
| 1067 | + else: |
| 1068 | + # Use pixmap-based rendering for arbitrary angles (aligned but slightly blurry) |
| 1069 | + self._drawRotatedTextWithAlignment(painter, lbl, pos, labelSize, rotation) |
| 1070 | + else: |
| 1071 | + # Use standard approach for non-rotated text |
| 1072 | + transform = self.labelTransformation(pos, labelSize) |
| 1073 | + painter.save() |
| 1074 | + painter.setRenderHint(QPainter.TextAntialiasing, True) |
| 1075 | + painter.setWorldTransform(transform, True) |
| 1076 | + lbl.draw(painter, QRect(QPoint(0, 0), labelSize.toSize())) |
| 1077 | + painter.restore() |
| 1078 | + |
| 1079 | + def _drawRotatedTextWithAlignment(self, painter, lbl, pos, labelSize, rotation): |
| 1080 | + """ |
| 1081 | + Draw rotated text with improved character alignment by rendering to an |
| 1082 | + intermediate pixmap and then rotating the pixmap instead of applying |
| 1083 | + transformation to text. |
| 1084 | + :param QPainter painter: Painter |
| 1085 | + :param QwtText lbl: Label text object |
| 1086 | + :param QPointF pos: Position where to paint the label |
| 1087 | + :param QSizeF labelSize: Size of the label |
| 1088 | + :param float rotation: Rotation angle in degrees |
| 1089 | + """ |
| 1090 | + # Create a pixmap to render the text without rotation first |
| 1091 | + text_size = labelSize.toSize() |
| 1092 | + if text_size.width() <= 0 or text_size.height() <= 0: |
| 1093 | + return |
| 1094 | + |
| 1095 | + # Add some padding to prevent edge clipping |
| 1096 | + padding = 2 |
| 1097 | + pixmap_size = text_size + QSize(padding * 2, padding * 2) |
| 1098 | + pixmap = QPixmap(pixmap_size) |
| 1099 | + pixmap.fill(Qt.transparent) |
| 1100 | + |
| 1101 | + # Render the text to the pixmap without any rotation |
| 1102 | + pixmap_painter = QPainter(pixmap) |
| 1103 | + pixmap_painter.setRenderHint(QPainter.TextAntialiasing, True) |
| 1104 | + |
| 1105 | + # Set font and color from QwtText |
| 1106 | + if lbl.testPaintAttribute(QwtText.PaintUsingTextFont): |
| 1107 | + pixmap_painter.setFont(lbl.font()) |
| 1108 | + else: |
| 1109 | + pixmap_painter.setFont(painter.font()) |
| 1110 | + |
| 1111 | + if ( |
| 1112 | + lbl.testPaintAttribute(QwtText.PaintUsingTextColor) |
| 1113 | + and lbl.color().isValid() |
| 1114 | + ): |
| 1115 | + pixmap_painter.setPen(lbl.color()) |
| 1116 | + else: |
| 1117 | + pixmap_painter.setPen(painter.pen()) |
| 1118 | + |
| 1119 | + # Draw text on pixmap without rotation for perfect character alignment |
| 1120 | + text_rect = QRect(padding, padding, text_size.width(), text_size.height()) |
| 1121 | + lbl.draw(pixmap_painter, text_rect) |
| 1122 | + pixmap_painter.end() |
| 1123 | + |
| 1124 | + # Now draw the pixmap with rotation |
1045 | 1125 | painter.save() |
1046 | | - painter.setWorldTransform(transform, True) |
1047 | | - lbl.draw(painter, QRect(QPoint(0, 0), labelSize.toSize())) |
| 1126 | + |
| 1127 | + # Get alignment flags for positioning |
| 1128 | + flags = self.labelAlignment() |
| 1129 | + if flags == 0: |
| 1130 | + flags = self.Flags[self.alignment()] |
| 1131 | + |
| 1132 | + # Calculate alignment offsets |
| 1133 | + if flags & Qt.AlignLeft: |
| 1134 | + x_offset = -labelSize.width() |
| 1135 | + elif flags & Qt.AlignRight: |
| 1136 | + x_offset = 0.0 |
| 1137 | + else: |
| 1138 | + x_offset = -(0.5 * labelSize.width()) |
| 1139 | + |
| 1140 | + if flags & Qt.AlignTop: |
| 1141 | + y_offset = -labelSize.height() |
| 1142 | + elif flags & Qt.AlignBottom: |
| 1143 | + y_offset = 0 |
| 1144 | + else: |
| 1145 | + y_offset = -(0.5 * labelSize.height()) |
| 1146 | + |
| 1147 | + # Apply transformation and draw the pre-rendered pixmap |
| 1148 | + painter.translate(pos.x(), pos.y()) |
| 1149 | + painter.rotate(rotation) |
| 1150 | + painter.translate(x_offset - padding, y_offset - padding) |
| 1151 | + |
| 1152 | + # Use smooth pixmap transform for better quality |
| 1153 | + painter.setRenderHint(QPainter.SmoothPixmapTransform, True) |
| 1154 | + painter.drawPixmap(0, 0, pixmap) |
| 1155 | + |
1048 | 1156 | painter.restore() |
1049 | 1157 |
|
1050 | 1158 | def boundingLabelRect(self, font, value): |
|
0 commit comments