本篇介绍用如何QPainter()画出一个可以显示分数的滑块控件,并使其能正确地响应鼠标事件,键盘事件以及自动调整尺寸。
代码如下:
import sys
from PyQt5.QtWidgets import *
import PyQt5.QtGui
from PyQt5.QtGui import QColor, QPalette, QFont, QFontMetricsF,QPainter,QBrush,QPen,QPolygonF
#和 QT4 不一样,QPoint, QPointF转到了QtCore子模块
from PyQt5.QtCore import Qt, pyqtSignal,QSize, QPoint, QPointF,QRectF
X11 = hasattr(PyQt5.QtGui, "qt_x11_wait_for_window_manager")#是否是Linux 类系统
class FractionSlider(QWidget): #自定义分数滑块类
XMARGIN =12.0
YMARGIN = 5.0
WSTRING ="999"
valueChanged = pyqtSignal(int, int)
def __init__(self, numerator = 0, denominator = 18, parent = None):
super().__init__(parent)
self.__numerator = numerator
self.__denominator = denominator
self.setFocusPolicy(Qt.WheelFocus)#切换到,点击到或者在上面使用滚轮时,获得焦点
#水平方向收缩至最小尺寸单可增加,竖直方向尺寸固定
self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed))
def decimal(self):
return self.__numerator / float(self.__denominator)
def fraction(self):
return self.__numerator, self.__denominator
def setFraction(self,numerator, denominator =None):
if denominator is not None:
if 3 <= denominator <= 60:
self.__denominator = denominator
else:
raise ValueError( "denominator out of range")
if 0 <= numerator <= self.__denominator:
self.__numerator = numerator
else:
raise ValueError("numerator out of range")
self.update() # 触发paint()事件
self.updateGeometry()#通知布局管理器重新计算和调整布局
print("%d/%d" % self.fraction())#
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self.moveSlider(event.x())
event.accept()
else:
QWidget.mousePressEvent(self, event)
def moveSlider(self, x):
span = self.width() - (FractionSlider.XMARGIN*2)
offset = span - x + FractionSlider.XMARGIN
numerator = int(round( self.__denominator * (1- offset / span)))
numerator = max(0, min (numerator ,self.__denominator))
if numerator != self.__numerator:
self.__numerator = numerator
#self.emit(SIGNAL("ValueChanged(int, int)"), self.__numerator, self.__denominator)
self.valueChanged[int, int].emit( self.__numerator, self.__denominator)
self.update()
print("%d/%d" % self.fraction())#
def mouseMoveEvent(self,event):
self.moveSlider(event.x())
def keyPressEvent(self, event):
change = 0
if event.key() == Qt.Key_Home:
change = -self.__denominator
elif event.key() in (Qt.Key_Up, Qt.Key_Right):
change = 1
elif event.key() == Qt.Key_PageUp:
change = (self.__denominator // 10) + 1
elif event.key() in (Qt.Key_Down, Qt.Key_Left):
change = -1
elif event.key() == Qt.Key_PageDown:
change = -((self.__denominator // 10) + 1)
elif event.key() == Qt.Key_End:
change = self.__denominator
if change:
numerator = self.__numerator
numerator += change
numerator = max(0, min(numerator, self.__denominator))
if numerator != self.__numerator:
self.__numerator = numerator
self.valueChanged.emit(self.__numerator, self.__denominator)
print("%d/%d" % self.fraction())#
self.update()
event.accept()
else:
QWidget.keyPressEvent(self, event)
def sizeHint(self):
return self.minimumSizeHint()
def minimumSizeHint(self):
font = self.font()
font.setPointSize(font.pointSize() -1)
fm = QFontMetricsF(font)
return QSize(fm.width(FractionSlider.WSTRING) * self.__denominator,
fm.height() * 4 + FractionSlider.YMARGIN)
def paintEvent(self, event=None):
font = self.font()
font.setPointSize(font.pointSize() - 1)
fm = QFontMetricsF(font)
fracWidth = fm.width(FractionSlider.WSTRING)
indent = fm.boundingRect("9").width() / 2.0
if not X11:
fracWidth *= 1.5
span = self.width() - (FractionSlider.XMARGIN * 2)
value = self.__numerator / float(self.__denominator)
painter = QPainter(self)#!注意这里有self,否则不显示任何图形
painter.setRenderHint(QPainter.Antialiasing)#抗锯齿
painter.setRenderHint(QPainter.TextAntialiasing)#文字抗锯齿
#
painter.setPen(self.palette().color(QPalette.Mid))#画笔
painter.setBrush(self.palette().brush(QPalette.AlternateBase))#画刷
painter.drawRect(self.rect())
segColor = QColor(Qt.green).darker(120)
segLineColor = segColor.darker()
painter.setPen(segLineColor)
painter.setBrush(segColor)
#绘制绿色矩形
painter.drawRect(FractionSlider.XMARGIN,FractionSlider.YMARGIN, span, fm.height())
textColor = self.palette().color(QPalette.Text)
segWidth = span / self.__denominator
segHeight = fm.height() * 2
nRect = fm.boundingRect(FractionSlider.WSTRING)
x = FractionSlider.XMARGIN
yOffset = segHeight + fm.height()
for i in range(self.__denominator + 1):#循环绘制刻度线,分子,分母和分数的横线
painter.setPen(segLineColor)
painter.drawLine(x, FractionSlider.YMARGIN, x, segHeight)#绘制刻度线
painter.setPen(textColor)
y = segHeight
rect = QRectF(nRect)
rect.moveCenter(QPointF(x, y + fm.height() / 2.0))
painter.drawText(rect, Qt.AlignCenter,str(i)) #分子
y = yOffset
rect.moveCenter(QPointF(x, y + fm.height() / 2.0))
painter.drawText(rect, Qt.AlignCenter, str(self.__denominator)) #分母
painter.drawLine(QPointF(rect.left() + indent, y), QPointF(rect.right() - indent, y))#横线
x += segWidth
#绘制黄色三角形
y = FractionSlider.YMARGIN - 0.5
triangle = [QPointF(value * span, y),
QPointF(value * span +2 * FractionSlider.XMARGIN , y),
QPointF((value * span) +FractionSlider.XMARGIN, fm.height())]
painter.setPen(Qt.yellow)
painter.setBrush(Qt.darkYellow)
painter.drawPolygon(QPolygonF(triangle))
class UI(QWidget):#主界面
def __init__(self, parent = None):
super().__init__(parent)
sliderLabel = QLabel("&Fraction")
self.slider = FractionSlider(denominator=18)
sliderLabel.setBuddy(self.slider)
denominatorLabel = QLabel("&Denominator")
self.denominatorSpinBox = QSpinBox()
denominatorLabel.setBuddy(self.denominatorSpinBox)
self.denominatorSpinBox.setRange(3, 60)
self.denominatorSpinBox.setValue(self.slider.fraction()[1])
self.denominatorSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
self.numeratorLabel = QLabel("Numerator")
self.numeratorLCD = QLCDNumber()
self.numeratorLCD.setSegmentStyle(QLCDNumber.Flat)
layout = QGridLayout()
layout.addWidget(sliderLabel, 0, 0)
layout.addWidget(self.slider, 0, 1, 1, 5)
layout.addWidget(self.numeratorLabel, 1, 0)
layout.addWidget(self.numeratorLCD, 1, 1)
layout.addWidget(denominatorLabel, 1, 2)
layout.addWidget(self.denominatorSpinBox, 1, 3)
self.setLayout(layout)
self.setWindowTitle("分数滑块控件")
self.slider.valueChanged[int,int].connect(self.numeratorLCD.display)#信号2个参数只接收一个
self.denominatorSpinBox.valueChanged[int].connect(self.valueChanged)
def valueChanged(self, denominator):
numerator = int(self.slider.decimal() * denominator)
self.slider.setFraction(numerator, denominator)
self.numeratorLCD.display(numerator)#显示分子
if __name__ == '__main__':
app = QApplication(sys.argv)
ui = UI()
ui.show()
sys.exit(app.exec_())
本篇的例子来自《Python Qt Gui 快速编程 ----PyQt 编程指南》,原文是PyQt4的,现已改为PyQt5 版本。
本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!