Skip to content

Commit 5cafca9

Browse files
committed
Fixed label positioning for rotated scale labels
1 parent 09c1d30 commit 5cafca9

File tree

3 files changed

+92
-5
lines changed

3 files changed

+92
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# PythonQwt Releases
22

3+
## Version 0.14.7
4+
5+
- Fixed label positioning for rotated scale labels - labels are now properly positioned to account for their rotated bounding box, preventing overlap with scale backbone and ticks
6+
37
## Version 0.14.6
48

59
- Fixed [Issue #100](https://github.com/PlotPyStack/PythonQwt/issues/100) - TypeError in `QwtSymbol.drawSymbol` method due to outdated `renderSymbols` call

qwt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
from qwt.text import QwtText # noqa: F401
5656
from qwt.toqimage import array_to_qimage as toQImage # noqa: F401
5757

58-
__version__ = "0.14.6"
58+
__version__ = "0.14.7"
5959
QWT_VERSION_STR = "6.1.5"
6060

6161

qwt/scale_draw.py

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
Qt,
3232
qFuzzyCompare,
3333
)
34-
from qtpy.QtGui import QFontMetrics, QPalette, QTransform
34+
from qtpy.QtGui import QFont, QFontMetrics, QPalette, QTransform
3535

3636
from qwt._math import qwtRadians
3737
from qwt.scale_div import QwtScaleDiv
@@ -760,6 +760,15 @@ def labelPosition(self, value):
760760
:param float value: Value
761761
:return: Position, where to paint a label
762762
"""
763+
# For backward compatibility, use a default font metrics approach
764+
# when rotation is involved
765+
if abs(self.labelRotation()) > 1e-6:
766+
# We need font information for proper rotation-aware positioning
767+
# Use QFontMetrics with a default font as fallback
768+
default_font = QFont()
769+
return self._labelPositionWithFont(default_font, value)
770+
771+
# Original implementation for non-rotated labels
763772
tval = self.scaleMap().transform(value)
764773
dist = self.spacing()
765774
if self.hasComponent(QwtAbstractScaleDraw.Backbone):
@@ -784,6 +793,80 @@ def labelPosition(self, value):
784793

785794
return QPointF(px, py)
786795

796+
def _labelPositionWithFont(self, font, value):
797+
"""
798+
Find the position where to paint a label, taking rotation into account.
799+
800+
:param QFont font: Font used for the label
801+
:param float value: Value
802+
:return: Position where to paint a label
803+
"""
804+
tval = self.scaleMap().transform(value)
805+
dist = self.spacing()
806+
if self.hasComponent(QwtAbstractScaleDraw.Backbone):
807+
dist += max([1, self.penWidth()])
808+
if self.hasComponent(QwtAbstractScaleDraw.Ticks):
809+
dist += self.tickLength(QwtScaleDiv.MajorTick)
810+
811+
# Add rotation-aware offset
812+
dist += self._rotatedLabelOffset(font, value)
813+
814+
px = 0
815+
py = 0
816+
if self.alignment() == self.RightScale:
817+
px = self.__data.pos.x() + dist
818+
py = tval
819+
elif self.alignment() == self.LeftScale:
820+
px = self.__data.pos.x() - dist
821+
py = tval
822+
elif self.alignment() == self.BottomScale:
823+
px = tval
824+
py = self.__data.pos.y() + dist
825+
elif self.alignment() == self.TopScale:
826+
px = tval
827+
py = self.__data.pos.y() - dist
828+
829+
return QPointF(px, py)
830+
831+
def _rotatedLabelOffset(self, font, value):
832+
"""
833+
Calculate the additional offset needed for a rotated label
834+
to avoid overlap with the scale backbone and ticks.
835+
836+
:param QFont font: Font used for the label
837+
:param float value: Value for which to calculate the offset
838+
:return: Additional offset distance
839+
"""
840+
rotation = self.labelRotation()
841+
if abs(rotation) < 1e-6: # No rotation, no additional offset needed
842+
return 0.0
843+
844+
lbl, labelSize = self.tickLabel(font, value)
845+
if lbl.isEmpty():
846+
return 0.0
847+
848+
# Convert rotation to radians
849+
angle = qwtRadians(rotation)
850+
cos_a = abs(math.cos(angle))
851+
sin_a = abs(math.sin(angle))
852+
853+
# Calculate the rotated bounding box dimensions
854+
width = labelSize.width()
855+
height = labelSize.height()
856+
rotated_width = width * cos_a + height * sin_a
857+
rotated_height = width * sin_a + height * cos_a
858+
859+
# Calculate additional offset based on scale alignment
860+
additional_offset = 0.0
861+
if self.alignment() in (self.LeftScale, self.RightScale):
862+
# For vertical scales, consider the horizontal extent of rotated label
863+
additional_offset = max(0, (rotated_width - width) * 0.5)
864+
else: # TopScale, BottomScale
865+
# For horizontal scales, consider the vertical extent of rotated label
866+
additional_offset = max(0, (rotated_height - height) * 0.5)
867+
868+
return additional_offset
869+
787870
def drawTick(self, painter, value, len_):
788871
"""
789872
Draw a tick
@@ -957,7 +1040,7 @@ def drawLabel(self, painter, value):
9571040
lbl, labelSize = self.tickLabel(painter.font(), value)
9581041
if lbl is None or lbl.isEmpty():
9591042
return
960-
pos = self.labelPosition(value)
1043+
pos = self._labelPositionWithFont(painter.font(), value)
9611044
transform = self.labelTransformation(pos, labelSize)
9621045
painter.save()
9631046
painter.setWorldTransform(transform, True)
@@ -982,7 +1065,7 @@ def boundingLabelRect(self, font, value):
9821065
lbl, labelSize = self.tickLabel(font, value)
9831066
if lbl.isEmpty():
9841067
return QRect()
985-
pos = self.labelPosition(value)
1068+
pos = self._labelPositionWithFont(font, value)
9861069
transform = self.labelTransformation(pos, labelSize)
9871070
return transform.mapRect(QRect(QPoint(0, 0), labelSize.toSize()))
9881071

@@ -1038,7 +1121,7 @@ def labelRect(self, font, value):
10381121
lbl, labelSize = self.tickLabel(font, value)
10391122
if not lbl or lbl.isEmpty():
10401123
return QRectF(0.0, 0.0, 0.0, 0.0)
1041-
pos = self.labelPosition(value)
1124+
pos = self._labelPositionWithFont(font, value)
10421125
transform = self.labelTransformation(pos, labelSize)
10431126
br = transform.mapRect(QRectF(QPointF(0, 0), labelSize))
10441127
br.translate(-pos.x(), -pos.y())

0 commit comments

Comments
 (0)