Python 可以在滚动条顶部添加文本吗?
我想在左端、右端和滑块上添加一些文本,如下图所示 我不明白如何在小部件上添加文本 这里是没有文本的Qscrollbar的最小示例Python 可以在滚动条顶部添加文本吗?,python,text,slider,pyqt5,scrollbar,Python,Text,Slider,Pyqt5,Scrollbar,我想在左端、右端和滑块上添加一些文本,如下图所示 我不明白如何在小部件上添加文本 这里是没有文本的Qscrollbar的最小示例 from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * import sys class Viewer(QMainWindow): def __init__(self, parent=None): super(Viewer, sel
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class Viewer(QMainWindow):
def __init__(self, parent=None):
super(Viewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.mainVBOX_param_scene = QVBoxLayout()
self.paramPlotV = QVBoxLayout()
self.horizontalSliders = QScrollBar(Qt.Horizontal)
self.horizontalSliders.setMinimum(0)
self.horizontalSliders.setMaximum(10)
self.horizontalSliders.setPageStep(1)
self.paramPlotV.addWidget(self.horizontalSliders)
self.centralWidget.setLayout(self.paramPlotV)
def main():
app = QApplication(sys.argv)
app.setStyle('Windows')
ex = Viewer(app)
ex.showMaximized()
sys.exit(app.exec())
if __name__ == '__main__':
main()
有两种可能的方法,它们都用于获取滑块的几何图形,子页面/添加页面矩形滑块外部和按钮内部的空间(如果可见) 子类QScrollBar和override paintEvent 这里我们重写滚动条的paintEvent,调用绘制滚动条小部件并在其上绘制文本的基类实现。 为了得到我们要绘制的矩形,我们创建了一个QStyleOptionSlider,它是一个QStyleOption子类,用于任何基于滑块的小部件,包括滚动条;包含QStyle绘制图形元素所需的所有信息,其子类允许QStyle了解如何绘制复杂元素(如滚动条)或控制任何鼠标事件的行为
class PaintTextScrollBar(QScrollBar):
preText = 'pre text'
postText = 'post text'
sliderText = 'slider'
def paintEvent(self, event):
# call the base class paintEvent, which will draw the scrollbar
super().paintEvent(event)
# create a suitable styleoption and "init" it to this instance
option = QStyleOptionSlider()
self.initStyleOption(option)
painter = QPainter(self)
# get the slider rectangle
sliderRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarSlider, self)
# if the slider text is wider than the slider width, adjust its size;
# note: it's always better to add some horizontal margin for text
textWidth = self.fontMetrics().width(self.sliderText)
if textWidth > sliderRect.width():
sideWidth = (textWidth - sliderRect.width()) / 2
sliderRect.adjust(-sideWidth, 0, sideWidth, 0)
painter.drawText(sliderRect, Qt.AlignCenter,
self.sliderText)
# get the "subPage" rectangle and draw the text
subPageRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarSubPage, self)
painter.drawText(subPageRect, Qt.AlignLeft|Qt.AlignVCenter, self.preText)
# get the "addPage" rectangle and draw its text
addPageRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarAddPage, self)
painter.drawText(addPageRect, Qt.AlignRight|Qt.AlignVCenter, self.postText)
这种方法非常有效,在大多数简单的情况下可能很好,但当文本宽度大于滑块控制柄的大小时,就会出现问题,因为Qt根据滑块的总体大小及其最小值和最大值之间的范围来决定滑块的范围。
虽然您可以像我在示例中所做的那样调整正在绘制文本的矩形的大小,但这远远不够完美:只要滑块文本太宽,它可能会绘制在前置和后置文本上,如果滑块靠近边缘,则会使整个滚动条非常难看,因为文本可能会覆盖箭头按钮:
注意:未调整文本矩形的结果将与上图中的第一个滚动条相同,文本将被剪切到滑块几何体
使用代理样式
是一个QStyle子代,它提供了一种简单的方法,只重写现有样式的方法,从而使子类化更容易。
我们最感兴趣的函数是,Qt使用它来绘制复杂的控件,如旋转框和滚动条。通过仅实现此函数,只要将自定义样式应用于标准QScrollBar,行为将与上面解释的paintEvent方法完全相同
代理风格真正有帮助的是能够改变几乎任何小部件的整体外观和行为。
为了能够充分利用它的大部分特性,我实现了另一个QScrollBar子类,允许更多的定制,同时覆盖了其他重要的QProxyStyle函数
class TextScrollBarStyle(QProxyStyle):
def drawComplexControl(self, control, option, painter, widget):
# call the base implementation which will draw anything Qt will ask
super().drawComplexControl(control, option, painter, widget)
# check if control type and orientation match
if control == QStyle.CC_ScrollBar and option.orientation == Qt.Horizontal:
# the option is already provided by the widget's internal paintEvent;
# from this point on, it's almost the same as explained above, but
# setting the pen might be required for some styles
painter.setPen(widget.palette().color(QPalette.WindowText))
margin = self.frameMargin(widget) + 1
sliderRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSlider, widget)
painter.drawText(sliderRect, Qt.AlignCenter, widget.sliderText)
subPageRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSubPage, widget)
subPageRect.setRight(sliderRect.left() - 1)
painter.save()
painter.setClipRect(subPageRect)
painter.drawText(subPageRect.adjusted(margin, 0, 0, 0),
Qt.AlignLeft|Qt.AlignVCenter, widget.preText)
painter.restore()
addPageRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarAddPage, widget)
addPageRect.setLeft(sliderRect.right() + 1)
painter.save()
painter.setClipRect(addPageRect)
painter.drawText(addPageRect.adjusted(0, 0, -margin, 0),
Qt.AlignRight|Qt.AlignVCenter, widget.postText)
painter.restore()
def frameMargin(self, widget):
# a helper function to get the default frame margin which is usually added
# to widgets and sub widgets that might look like a frame, which usually
# includes the slider of a scrollbar
option = QStyleOptionFrame()
option.initFrom(widget)
return self.pixelMetric(QStyle.PM_DefaultFrameWidth, option, widget)
def subControlRect(self, control, option, subControl, widget):
rect = super().subControlRect(control, option, subControl, widget)
if (control == QStyle.CC_ScrollBar
and isinstance(widget, StyledTextScrollBar)
and option.orientation == Qt.Horizontal):
if subControl == QStyle.SC_ScrollBarSlider:
# get the *default* groove rectangle (the space in which the
# slider can move)
grooveRect = super().subControlRect(control, option,
QStyle.SC_ScrollBarGroove, widget)
# ensure that the slider is wide enough for its text
width = max(rect.width(),
widget.sliderWidth + self.frameMargin(widget))
# compute the position of the slider according to the
# scrollbar value and available space (the "groove")
pos = self.sliderPositionFromValue(widget.minimum(),
widget.maximum(), widget.sliderPosition(),
grooveRect.width() - width)
# return the new rectangle
return QRect(grooveRect.x() + pos,
(grooveRect.height() - rect.height()) / 2,
width, rect.height())
elif subControl == QStyle.SC_ScrollBarSubPage:
# adjust the rectangle based on the slider
sliderRect = self.subControlRect(
control, option, QStyle.SC_ScrollBarSlider, widget)
rect.setRight(sliderRect.left())
elif subControl == QStyle.SC_ScrollBarAddPage:
# same as above
sliderRect = self.subControlRect(
control, option, QStyle.SC_ScrollBarSlider, widget)
rect.setLeft(sliderRect.right())
return rect
def hitTestComplexControl(self, control, option, pos, widget):
if control == QStyle.CC_ScrollBar:
# check click events against the resized slider
sliderRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSlider, widget)
if pos in sliderRect:
return QStyle.SC_ScrollBarSlider
return super().hitTestComplexControl(control, option, pos, widget)
class StyledTextScrollBar(QScrollBar):
def __init__(self, sliderText='', preText='', postText=''):
super().__init__(Qt.Horizontal)
self.setStyle(TextScrollBarStyle())
self.preText = preText
self.postText = postText
self.sliderText = sliderText
self.sliderTextMargin = 2
self.sliderWidth = self.fontMetrics().width(sliderText) + self.sliderTextMargin + 2
def setPreText(self, text):
self.preText = text
self.update()
def setPostText(self, text):
self.postText = text
self.update()
def setSliderText(self, text):
self.sliderText = text
self.sliderWidth = self.fontMetrics().width(text) + self.sliderTextMargin + 2
def setSliderTextMargin(self, margin):
self.sliderTextMargin = margin
self.sliderWidth = self.fontMetrics().width(self.sliderText) + margin + 2
def sizeHint(self):
# give the scrollbar enough height for the font
hint = super().sizeHint()
if hint.height() < self.fontMetrics().height() + 4:
hint.setHeight(self.fontMetrics().height() + 4)
return hint
有两种可能的方法,它们都用于获取滑块的几何图形,子页面/添加页面矩形滑块外部和按钮内部的空间(如果可见) 子类QScrollBar和override paintEvent 这里我们重写滚动条的paintEvent,调用绘制滚动条小部件并在其上绘制文本的基类实现。 为了得到我们要绘制的矩形,我们创建了一个QStyleOptionSlider,它是一个QStyleOption子类,用于任何基于滑块的小部件,包括滚动条;包含QStyle绘制图形元素所需的所有信息,其子类允许QStyle了解如何绘制复杂元素(如滚动条)或控制任何鼠标事件的行为
class PaintTextScrollBar(QScrollBar):
preText = 'pre text'
postText = 'post text'
sliderText = 'slider'
def paintEvent(self, event):
# call the base class paintEvent, which will draw the scrollbar
super().paintEvent(event)
# create a suitable styleoption and "init" it to this instance
option = QStyleOptionSlider()
self.initStyleOption(option)
painter = QPainter(self)
# get the slider rectangle
sliderRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarSlider, self)
# if the slider text is wider than the slider width, adjust its size;
# note: it's always better to add some horizontal margin for text
textWidth = self.fontMetrics().width(self.sliderText)
if textWidth > sliderRect.width():
sideWidth = (textWidth - sliderRect.width()) / 2
sliderRect.adjust(-sideWidth, 0, sideWidth, 0)
painter.drawText(sliderRect, Qt.AlignCenter,
self.sliderText)
# get the "subPage" rectangle and draw the text
subPageRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarSubPage, self)
painter.drawText(subPageRect, Qt.AlignLeft|Qt.AlignVCenter, self.preText)
# get the "addPage" rectangle and draw its text
addPageRect = self.style().subControlRect(QStyle.CC_ScrollBar,
option, QStyle.SC_ScrollBarAddPage, self)
painter.drawText(addPageRect, Qt.AlignRight|Qt.AlignVCenter, self.postText)
这种方法非常有效,在大多数简单的情况下可能很好,但当文本宽度大于滑块控制柄的大小时,就会出现问题,因为Qt根据滑块的总体大小及其最小值和最大值之间的范围来决定滑块的范围。
虽然您可以像我在示例中所做的那样调整正在绘制文本的矩形的大小,但这远远不够完美:只要滑块文本太宽,它可能会绘制在前置和后置文本上,如果滑块靠近边缘,则会使整个滚动条非常难看,因为文本可能会覆盖箭头按钮:
注意:未调整文本矩形的结果将与上图中的第一个滚动条相同,文本将被剪切到滑块几何体
使用代理样式
是一个QStyle子代,它提供了一种简单的方法,只重写现有样式的方法,从而使子类化更容易。
我们最感兴趣的函数是,Qt使用它来绘制复杂的控件,如旋转框和滚动条。通过仅实现此函数,只要将自定义样式应用于标准QScrollBar,行为将与上面解释的paintEvent方法完全相同
代理风格真正有帮助的是能够改变几乎任何小部件的整体外观和行为。
为了能够充分利用它的大部分特性,我实现了另一个QScrollBar子类,允许更多的定制,同时覆盖了其他重要的QProxyStyle函数
class TextScrollBarStyle(QProxyStyle):
def drawComplexControl(self, control, option, painter, widget):
# call the base implementation which will draw anything Qt will ask
super().drawComplexControl(control, option, painter, widget)
# check if control type and orientation match
if control == QStyle.CC_ScrollBar and option.orientation == Qt.Horizontal:
# the option is already provided by the widget's internal paintEvent;
# from this point on, it's almost the same as explained above, but
# setting the pen might be required for some styles
painter.setPen(widget.palette().color(QPalette.WindowText))
margin = self.frameMargin(widget) + 1
sliderRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSlider, widget)
painter.drawText(sliderRect, Qt.AlignCenter, widget.sliderText)
subPageRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSubPage, widget)
subPageRect.setRight(sliderRect.left() - 1)
painter.save()
painter.setClipRect(subPageRect)
painter.drawText(subPageRect.adjusted(margin, 0, 0, 0),
Qt.AlignLeft|Qt.AlignVCenter, widget.preText)
painter.restore()
addPageRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarAddPage, widget)
addPageRect.setLeft(sliderRect.right() + 1)
painter.save()
painter.setClipRect(addPageRect)
painter.drawText(addPageRect.adjusted(0, 0, -margin, 0),
Qt.AlignRight|Qt.AlignVCenter, widget.postText)
painter.restore()
def frameMargin(self, widget):
# a helper function to get the default frame margin which is usually added
# to widgets and sub widgets that might look like a frame, which usually
# includes the slider of a scrollbar
option = QStyleOptionFrame()
option.initFrom(widget)
return self.pixelMetric(QStyle.PM_DefaultFrameWidth, option, widget)
def subControlRect(self, control, option, subControl, widget):
rect = super().subControlRect(control, option, subControl, widget)
if (control == QStyle.CC_ScrollBar
and isinstance(widget, StyledTextScrollBar)
and option.orientation == Qt.Horizontal):
if subControl == QStyle.SC_ScrollBarSlider:
# get the *default* groove rectangle (the space in which the
# slider can move)
grooveRect = super().subControlRect(control, option,
QStyle.SC_ScrollBarGroove, widget)
# ensure that the slider is wide enough for its text
width = max(rect.width(),
widget.sliderWidth + self.frameMargin(widget))
# compute the position of the slider according to the
# scrollbar value and available space (the "groove")
pos = self.sliderPositionFromValue(widget.minimum(),
widget.maximum(), widget.sliderPosition(),
grooveRect.width() - width)
# return the new rectangle
return QRect(grooveRect.x() + pos,
(grooveRect.height() - rect.height()) / 2,
width, rect.height())
elif subControl == QStyle.SC_ScrollBarSubPage:
# adjust the rectangle based on the slider
sliderRect = self.subControlRect(
control, option, QStyle.SC_ScrollBarSlider, widget)
rect.setRight(sliderRect.left())
elif subControl == QStyle.SC_ScrollBarAddPage:
# same as above
sliderRect = self.subControlRect(
control, option, QStyle.SC_ScrollBarSlider, widget)
rect.setLeft(sliderRect.right())
return rect
def hitTestComplexControl(self, control, option, pos, widget):
if control == QStyle.CC_ScrollBar:
# check click events against the resized slider
sliderRect = self.subControlRect(control, option,
QStyle.SC_ScrollBarSlider, widget)
if pos in sliderRect:
return QStyle.SC_ScrollBarSlider
return super().hitTestComplexControl(control, option, pos, widget)
class StyledTextScrollBar(QScrollBar):
def __init__(self, sliderText='', preText='', postText=''):
super().__init__(Qt.Horizontal)
self.setStyle(TextScrollBarStyle())
self.preText = preText
self.postText = postText
self.sliderText = sliderText
self.sliderTextMargin = 2
self.sliderWidth = self.fontMetrics().width(sliderText) + self.sliderTextMargin + 2
def setPreText(self, text):
self.preText = text
self.update()
def setPostText(self, text):
self.postText = text
self.update()
def setSliderText(self, text):
self.sliderText = text
self.sliderWidth = self.fontMetrics().width(text) + self.sliderTextMargin + 2
def setSliderTextMargin(self, margin):
self.sliderTextMargin = margin
self.sliderWidth = self.fontMetrics().width(self.sliderText) + margin + 2
def sizeHint(self):
# give the scrollbar enough height for the font
hint = super().sizeHint()
if hint.height() < self.fontMetrics().height() + 4:
hint.setHeight(self.fontMetrics().height() + 4)
return hint
是的,有可能:。我不知道怎么做
我可以在python中使用它。我是否应该编写一个从QScrollBar继承的新类?@ymmx是…………是的,有可能:。我不知道如何在python中使用它。我应该写一个从QScrollBar继承的新类吗?@ymmx是的…………哇!正是我想要的。我不可能找到这么短的解决方案。我在类中添加了三个变量,用于在滑块移动时修改文本。非常感谢。为了让文本超过滑块控制柄的限制,我刚刚用sliderRect.adjust-200,0,200,0调整了sliderRect的大小。这给了文本足够的空间,我强烈建议不要这样做:记住这样做只会扩大文本的可用空间,而不是滑块。最终,文本可能会覆盖滑块几何体的边界,可能会覆盖其他两页文本矩形,甚至滚动条按钮,使控件使用起来非常不舒服,外观也不太好看。即使您应用了一些剪辑,当滑块靠近边缘时,这显然会隐藏部分滑块文本。@ymmx我已经用更好的QProxyStyle实现更新了我的答案,允许在文本更宽的情况下更好地调整滑块大小。非常感谢。我不得不说,我可能需要几周的时间才能找到一个合适的方法。在这个问题之前,我不知道QProxyStyle,但它似乎非常有用。我将尝试找到一些关于它的教程。非常感谢。哇!正是我想要的。我不可能找到这么短的解决方案。我在类中添加了三个变量,用于在滑块移动时修改文本。非常感谢。为了让文本超过滑块控制柄的限制,我刚刚用sliderRect.adjust-200,0,200,0调整了sliderRect的大小。这给了文本足够的空间,我强烈建议不要这样做:记住这样做只会扩大文本的可用空间,而不是滑块。最终,文本可能会覆盖滑块几何体的边界,可能会覆盖其他两页文本矩形,甚至滚动条按钮,使控件使用起来非常不舒服,外观也不太好看。即使您应用了一些剪辑,当滑块靠近边缘时,这显然会隐藏部分滑块文本。@ymmx我已经用更好的QProxyStyle实现更新了我的答案,允许在文本更宽的情况下更好地调整滑块大小。非常感谢。我不得不说,我可能需要几周的时间才能找到一个合适的方法。在这个问题之前,我不知道QProxyStyle,但它似乎非常有用。我将尝试找到一些关于它的教程。谢谢。