PyQt6标尺刻度方向修改:如何让刻度根部朝向视图内侧
PyQt6 Ruler控件:调整刻度根部位置与动态设置基准方向
以下是针对你的需求修改后的完整代码,同时附上关键修改说明:
from PyQt6.QtWidgets import QWidget, QApplication from PyQt6.QtGui import QPainter, QPen, QColor, QFont from PyQt6.QtCore import Qt, QPoint, QPointF RULER_BREADTH = 20 # 补充定义标尺宽度常量 class Ruler(QWidget): def __init__(self, orientation=Qt.Orientation.Horizontal, base_direction=None, parent=None): super().__init__(parent) self._orientation = orientation # 设置默认基准方向 if base_direction is None: self._base_direction = Qt.Edge.BottomEdge if orientation == Qt.Orientation.Horizontal else Qt.Edge.RightEdge else: self._base_direction = base_direction self._origin = 0.0 self._unit = 1.0 self._zoom = 1.0 self._mouse_tracking = False self._cursor_pos = QPoint() self.setMouseTracking(True) f = QFont("Courier", 9) f.setStyleHint(QFont.StyleHint.TypeWriter) self.setFont(f) # 固定控件尺寸,预留标签空间 if orientation == Qt.Orientation.Horizontal: self.setFixedHeight(RULER_BREADTH + 5) else: self.setFixedWidth(RULER_BREADTH + 5) def minimumSizeHint(self): return self.sizeHint() def sizeHint(self): if self._orientation == Qt.Orientation.Horizontal: return super().sizeHint().expandedTo(self.minimumSizeHint()).boundedTo(self.minimumSizeHint()) return super().sizeHint().expandedTo(self.minimumSizeHint()).boundedTo(self.minimumSizeHint()) def paintEvent(self, event): painter = QPainter(self) painter.setRenderHints( QPainter.RenderHint.Antialiasing | QPainter.RenderHint.TextAntialiasing ) # 填充背景 painter.fillRect(self.rect(), QColor(236, 233, 216)) # 绘制不同层级刻度 self._draw_scale(painter, mm_interval=1, tick_length=RULER_BREADTH / 3, draw_labels=False) self._draw_scale(painter, mm_interval=5, tick_length=RULER_BREADTH * 2 / 3, draw_labels=False) self._draw_scale(painter, mm_interval=10, tick_length=RULER_BREADTH, draw_labels=True) # 绘制鼠标位置指示器 if self._mouse_tracking: painter.setOpacity(0.4) self._draw_mouse_tick(painter) painter.setOpacity(1.0) # 绘制内边框(刻度根部参考线) painter.setPen(QPen(Qt.GlobalColor.black, 2)) if self._orientation == Qt.Orientation.Horizontal: line_start = self.rect().topLeft() if self._base_direction == Qt.Edge.TopEdge else self.rect().bottomLeft() line_end = self.rect().topRight() if self._base_direction == Qt.Edge.TopEdge else self.rect().bottomRight() else: line_start = self.rect().topLeft() if self._base_direction == Qt.Edge.LeftEdge else self.rect().topRight() line_end = self.rect().bottomLeft() if self._base_direction == Qt.Edge.LeftEdge else self.rect().bottomRight() painter.drawLine(line_start, line_end) def _draw_scale(self, painter, mm_interval, tick_length, draw_labels=False): is_horz = (self._orientation == Qt.Orientation.Horizontal) length = self.width() if is_horz else self.height() # 计算像素与毫米的转换比例 screen = QApplication.primaryScreen() dpi = screen.logicalDotsPerInchX() if is_horz else screen.logicalDotsPerInchY() px_per_mm = dpi / 25.4 step_pix = mm_interval * px_per_mm * self._zoom # 计算可见刻度范围 first_tick = int(self._origin // step_pix) - 1 last_tick = int((self._origin + length) // step_pix) + 1 painter.setPen(QPen(Qt.GlobalColor.red, 1)) for tick in range(first_tick, last_tick + 1): scene_pos = tick * step_pix screen_pos = scene_pos - self._origin if screen_pos < 0 or screen_pos > length: continue # 根据基准方向绘制刻度(根部对齐内边框) if is_horz: if self._base_direction == Qt.Edge.BottomEdge: start_y = self.height() end_y = self.height() - tick_length else: start_y = 0 end_y = tick_length painter.drawLine(QPointF(screen_pos, start_y), QPointF(screen_pos, end_y)) # 绘制刻度标签 if draw_labels and (tick % (10 // mm_interval) == 0): label = str(abs(tick * mm_interval)) painter.setPen(QPen(Qt.GlobalColor.black)) x = int(screen_pos + 2) y = int(end_y - 10) if self._base_direction == Qt.Edge.BottomEdge else int(end_y + 10) painter.drawText(x, y, label) else: if self._base_direction == Qt.Edge.RightEdge: start_x = self.width() end_x = self.width() - tick_length else: start_x = 0 end_x = tick_length painter.drawLine(QPointF(start_x, screen_pos), QPointF(end_x, screen_pos)) # 绘制刻度标签 if draw_labels and (tick % (10 // mm_interval) == 0): label = str(abs(tick * mm_interval)) painter.setPen(QPen(Qt.GlobalColor.black)) painter.save() if self._base_direction == Qt.Edge.RightEdge: tx, ty = int(end_x - 2), int(screen_pos + 2) painter.translate(tx, ty) painter.rotate(-90) else: tx, ty = int(end_x + 2), int(screen_pos + 2) painter.translate(tx, ty) painter.rotate(90) painter.drawText(0, 0, label) painter.restore() def _draw_mouse_tick(self, painter): if self._orientation == Qt.Orientation.Horizontal: x = self._cursor_pos.x() painter.drawLine(QPointF(x, 0), QPointF(x, self.height())) else: y = self._cursor_pos.y() painter.drawLine(QPointF(0, y), QPointF(self.width(), y)) def mouseMoveEvent(self, event): self._cursor_pos = event.position().toPoint() self.update() super().mouseMoveEvent(event) def setOrigin(self, val): self._origin = val self.update() def setUnit(self, val): self._unit = val self.update() def setZoom(self, val): self._zoom = val self.update() def setMouseTrack(self, track: bool): self._mouse_tracking = track self.update() def setBaseDirection(self, direction: Qt.Edge): # 校验基准方向与标尺方向的匹配性 if self._orientation == Qt.Orientation.Horizontal: if direction not in (Qt.Edge.TopEdge, Qt.Edge.BottomEdge): raise ValueError("水平标尺仅支持TopEdge或BottomEdge作为基准方向") else: if direction not in (Qt.Edge.LeftEdge, Qt.Edge.RightEdge): raise ValueError("垂直标尺仅支持LeftEdge或RightEdge作为基准方向") self._base_direction = direction self.update() @staticmethod def toMM(orientation: Qt.Orientation) -> float: screen = QApplication.primaryScreen() dpi = screen.logicalDotsPerInchX() if orientation == Qt.Orientation.Horizontal else screen.logicalDotsPerInchY() return 25.4 / dpi
关键修改说明
1. 实现刻度根部对齐内边框
- 水平标尺:根据基准方向,从顶部或底部内边框开始,向控件内部绘制刻度(替代原有的从顶部向外延伸逻辑)
- 垂直标尺:根据基准方向,从左侧或右侧内边框开始,向控件内部绘制刻度(替代原有的从左侧向外延伸逻辑)
- 同步调整刻度标签的位置,确保标签始终显示在控件内部
2. 动态设置基准方向
- 新增
_base_direction属性,支持水平标尺切换TopEdge/BottomEdge,垂直标尺切换LeftEdge/RightEdge - 新增
setBaseDirection方法,支持动态修改基准方向,并添加参数合法性校验 - 内边框的绘制位置跟随基准方向变化,始终作为刻度的根部参考线
使用示例
if __name__ == "__main__": app = QApplication([]) # 创建水平标尺,基准方向为顶部 ruler_horz_top = Ruler(Qt.Orientation.Horizontal, Qt.Edge.TopEdge) ruler_horz_top.setZoom(2) ruler_horz_top.show() # 创建垂直标尺,基准方向为左侧 ruler_vert_left = Ruler(Qt.Orientation.Vertical, Qt.Edge.LeftEdge) ruler_vert_left.setZoom(2) ruler_vert_left.show() # 动态切换基准方向 # ruler_horz_top.setBaseDirection(Qt.Edge.BottomEdge) app.exec()
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

