如何覆盖 qscrollbar onclick 默认行为
how to override qscrollbar onclick default behaviour
当您点击 QScrollBar 的 "page control area"(图像上的 'c')时,它会滚动一页。我想要的是让它滚动到完整位置,就像您选择 "Scroll here" 上下文菜单项时一样。
这是个很有趣的问题。为了找到答案,我们需要看一下 QScrollBar source 并找出两件事:
- 如何判断滚动条的哪一部分被点击了;
- 如何触发 "Scroll Here" 行为。
第一个问题的答案在于QScrollBar::mousePressEvent
实施。事实证明 QStyle::hitTestComplexControl
正是我们需要的。第二个问题呢,只要搜索"Scroll here",你会看到QScrollBarPrivate::pixelPosToRangeValue
是用来将事件位置转换为滑块值的。不幸的是,我们无法访问这个私有 class 的函数,所以我们不得不重新实现它。现在让我们应用获得的知识并在 subclass:
中实现新的行为
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()
适合我,python 3.4 pyqt 5.4。
pixelPosToRangeValue取自qt源码
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)
当您点击 QScrollBar 的 "page control area"(图像上的 'c')时,它会滚动一页。我想要的是让它滚动到完整位置,就像您选择 "Scroll here" 上下文菜单项时一样。
这是个很有趣的问题。为了找到答案,我们需要看一下 QScrollBar source 并找出两件事:
- 如何判断滚动条的哪一部分被点击了;
- 如何触发 "Scroll Here" 行为。
第一个问题的答案在于QScrollBar::mousePressEvent
实施。事实证明 QStyle::hitTestComplexControl
正是我们需要的。第二个问题呢,只要搜索"Scroll here",你会看到QScrollBarPrivate::pixelPosToRangeValue
是用来将事件位置转换为滑块值的。不幸的是,我们无法访问这个私有 class 的函数,所以我们不得不重新实现它。现在让我们应用获得的知识并在 subclass:
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()
适合我,python 3.4 pyqt 5.4。
pixelPosToRangeValue取自qt源码
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)