Notodontidae 发表于 2024-9-17 22:19:19

pyqt5 QSlider实现数轴但无法在数轴下方显示坐标对应的数值

做了个用QSlider实现[-10,10]的数轴类,但是并不能实现数轴下方显示对应的整数。
求修正与优化其中这一段负责显示数轴下方的整数的代码:
            text = str(value)
            font_metrics = painter.fontMetrics()
            text_width = font_metrics.width(text)
            painter.drawText(x - text_width // 2, slider_height - 30, text)

这一段代码真不知道如何改。。尝试减少了数轴显示的数字的数量也依旧不能显示。不知如何解决

附上全部代码:
import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSlider, QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QFontMetrics

class NumberLineSlider(QSlider):
    def __init__(self, min_value, max_value, *args, **kwargs):
      super().__init__(Qt.Horizontal, *args, **kwargs)
      self.min_value = min_value
      self.max_value = max_value
      self.setMinimum(min_value)
      self.setMaximum(max_value)
      self.setTickPosition(QSlider.TicksBelow)
      self.setTickInterval(1)# 设置每个刻度的间隔为1

    def paintEvent(self, event):
      super().paintEvent(event)
      painter = QPainter(self)
      painter.setRenderHint(QPainter.Antialiasing)

      # 获取当前slider的大小
      slider_width = self.width()
      slider_height = self.height()

      # 绘制每个整数标记
      for value in range(self.min_value, self.max_value + 1):
            # 计算每个数值的位置
            x = self.position_for_value(value, slider_width)
            # 绘制刻度线
            painter.drawLine(x, slider_height - 20, x, slider_height - 10)
            
            # 绘制数值文本
            text = str(value)
            font_metrics = painter.fontMetrics()
            text_width = font_metrics.width(text)
            painter.drawText(x - text_width // 2, slider_height - 30, text)

    def position_for_value(self, value, slider_width):
      """计算给定数值在滑块上的 x 坐标"""
      slider_min = self.minimum()
      slider_max = self.maximum()
      # 计算数值相对slider宽度的位置
      return int((value - slider_min) / (slider_max - slider_min) * (slider_width - 20)) + 10

class MainWindow(QWidget):
    def __init__(self):
      super().__init__()

      # 设置数轴范围,从 -10 到 10
      self.slider = NumberLineSlider(-10, 10)
      self.label = QLabel("Move the slider to see the value")

      # 当滑块移动时,更新标签显示的值
      self.slider.valueChanged.connect(self.on_slider_value_changed)

      # 布局
      layout = QVBoxLayout()
      layout.addWidget(self.slider)
      layout.addWidget(self.label)
      self.setLayout(layout)

    def on_slider_value_changed(self, value):
      # 更新标签显示当前滑块值
      self.label.setText(f"Value: {value}")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

FishC 发表于 2024-9-17 22:24:22

看你的代码,主要的问题在于计算 QSlider 的数值位置和进行drawText操作的地方。这里给你一个修正和优化的版本,希望能帮助你解决问题。

import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QSlider, QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPainter, QFontMetrics

class NumberLineSlider(QSlider):
    def __init__(self, min_value, max_value, *args, **kwargs):
      super().__init__(Qt.Horizontal, *args, **kwargs)
      self.min_value = min_value
      self.max_value = max_value
      self.setMinimum(min_value)
      self.setMaximum(max_value)
      self.setTickPosition(QSlider.TicksBelow)
      self.setTickInterval(1)# 设置每个刻度的间隔为1
   
    def paintEvent(self, event):
      super().paintEvent(event)
      painter = QPainter(self)
      painter.setRenderHint(QPainter.Antialiasing)
      
      slider_width = self.width()
      slider_height = self.height()
      
      for value in range(self.min_value, self.max_value + 1):
            x = self.position_for_value(value, slider_width)
            painter.drawLine(x, slider_height - 20, x, slider_height - 10)
            
            text = str(value)
            font_metrics = painter.fontMetrics()
            text_width = font_metrics.horizontalAdvance(text)# 更新点:使用 horizontalAdvance 而不是 width
            painter.drawText(x - text_width // 2, slider_height - 30, text)
   
    def position_for_value(self, value, slider_width):
      """计算给定数值在滑块上的 x 坐标"""
      slider_min = self.minimum()
      slider_max = self.maximum()
      if slider_max == slider_min:
            return 0
      return int(slider_width * (value - slider_min) / (slider_max - slider_min))

# 主窗口
class MainWindow(QWidget):
    def __init__(self):
      super().__init__()
      self.setWindowTitle('Number Line Slider Example')
      self.setGeometry(100, 100, 600, 100)
      
      layout = QVBoxLayout()
      
      self.slider = NumberLineSlider(0, 10)
      layout.addWidget(self.slider)
      
      self.value_label = QLabel("0")
      layout.addWidget(self.value_label)
      
      self.slider.valueChanged.connect(self.update_label)
      
      self.setLayout(layout)
      
    def update_label(self, value):
      self.value_label.setText(str(value))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

主要修改点:
1. 使用 `font_metrics.horizontalAdvance(text)` 而不是 `font_metrics.width(text)`。`horizontalAdvance` 是计算字符串显示长度的更现代化方法。
2. 增加了超出显示范围的处理。比如当 `self.minimum()` 和 `self.maximum()` 值相等时防止除以零错误。
3. 为方便测试,增加一个主窗口类 `MainWindow`, 承载 `NumberLineSlider` 并且显示当前的 Slider 值。

希望对你有帮助!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页: [1]
查看完整版本: pyqt5 QSlider实现数轴但无法在数轴下方显示坐标对应的数值