如何覆盖qscrollbar onclick默认行为

时间:2015-04-17 22:15:56

标签: qt pyqt

点击QScrollBar"页面控制区" (' c'在图片上),它会滚动一页。我想要的是让它滚动到完整,就像你选择"滚动到这里"上下文菜单项。

enter image description here

2 个答案:

答案 0 :(得分:2)

这是一个非常有趣的问题。要找到答案,我们需要查看QScrollBar source并找出两件事:

  • 如何确定单击滚动条的哪个部分;
  • 如何触发"滚动到这里"行为。

第一个问题的答案在于QScrollBar::mousePressEvent实施。事实证明,QStyle::hitTestComplexControl正是我们所需要的。第二个问题是什么,只需搜索"滚动到这里"并且您将看到QScrollBarPrivate::pixelPosToRangeValue用于将事件位置转换为滑块值。不幸的是,我们无法访问此私人课程的功能,因此我们不得不重新实施它。现在让我们应用已获得的知识并在子类中实现新行为:

import sys
from PyQt4 import QtCore, QtGui


class ModifiedScrollBar(QtGui.QScrollBar):

  def __init__(self, parent = None):
    super(ModifiedScrollBar, self).__init__(parent)

  def mousePressEvent(self, event):
    if event.button() == QtCore.Qt.LeftButton:
      opt = QtGui.QStyleOptionSlider()
      self.initStyleOption(opt)
      control = self.style().hitTestComplexControl(QtGui.QStyle.CC_ScrollBar, opt, 
                                                   event.pos(), self)
      if (control == QtGui.QStyle.SC_ScrollBarAddPage or 
          control == QtGui.QStyle.SC_ScrollBarSubPage):
        # scroll here
        gr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt, 
            QtGui.QStyle.SC_ScrollBarGroove, self)
        sr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt, 
            QtGui.QStyle.SC_ScrollBarSlider, self)
        if self.orientation() == QtCore.Qt.Horizontal:
          pos = event.pos().x()
          sliderLength = sr.width()
          sliderMin = gr.x()
          sliderMax = gr.right() - sliderLength + 1
          if (self.layoutDirection() == QtCore.Qt.RightToLeft):
              opt.upsideDown = not opt.upsideDown
        else:
          pos = event.pos().y()
          sliderLength = sr.height()
          sliderMin = gr.y()
          sliderMax = gr.bottom() - sliderLength + 1
        self.setValue(QtGui.QStyle.sliderValueFromPosition(
            self.minimum(), self.maximum(), pos - sliderMin, 
            sliderMax - sliderMin, opt.upsideDown))
        return

    return super(ModifiedScrollBar, self).mousePressEvent(event)


def main():
  app = QtGui.QApplication(sys.argv)
  edit = QtGui.QTextEdit()
  #uncomment for testing horizontal scrollbar
  #edit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
  edit.setPlainText("Lorem ipsum...")
  edit.setVerticalScrollBar(ModifiedScrollBar())
  edit.setHorizontalScrollBar(ModifiedScrollBar())
  edit.show()

  sys.exit(app.exec_())


if __name__ == '__main__':
  main()

答案 1 :(得分:0)

适用于我,python 3.4 pyqt 5.4。

pixelPosToRangeValue取自qt source

def wrapEF(ef):
    w = QObject()
    w.eventFilter = ef
    return w

def sbEventFilter(s, e):
    q = s
    if (e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton
        or e.type() == QEvent.MouseButtonDblClick):
        #pixelPosToRangeValue(pos)
        opt = QStyleOptionSlider()
        q.initStyleOption(opt)
        gr = q.style().subControlRect(QStyle.CC_ScrollBar, opt, 
                                               QStyle.SC_ScrollBarGroove, q)
        sr = q.style().subControlRect(QStyle.CC_ScrollBar, opt, 
                                               QStyle.SC_ScrollBarSlider, q)
        if q.orientation() == Qt.Horizontal:
            sliderLength = sr.width()
            sliderMin = gr.x()
            sliderMax = gr.right() - sliderLength + 1
            if q.layoutDirection() == Qt.RightToLeft:
                opt.upsideDown = not opt.upsideDown
            dt = sr.width()/2
            pos = e.pos().x()
        else:
            sliderLength = sr.height()
            sliderMin = gr.y()
            sliderMax = gr.bottom() - sliderLength + 1
            dt = sr.height()/2
            pos = e.pos().y()
        r = QStyle.sliderValueFromPosition(q.minimum(), q.maximum(),
                                           pos - sliderMin - dt, 
                                           sliderMax - sliderMin, opt.upsideDown)
        #pixelPosToRangeValue, 
        q.setValue(r)
    return q.eventFilter(s, e)


self.scrollBarEF = wrapEF(sbEventFilter)
self.hscrollbar.installEventFilter(self.scrollBarEF)
self.vscrollbar.installEventFilter(self.scrollBarEF)