《PyQt5 快速编程》例子注释

时间:2021-04-01 23:02:04

Fraction Slider.py的qt4代码修改后的qt5版本

import platform
import PyQt5
from PyQt5.QtCore import QPointF,QRectF,QSize,Qt,QObject,pyqtSignal
from PyQt5.QtGui import QColor,QFont,QFontMetricsF,QPainter,QPalette,QPolygonF,QKeyEvent
from PyQt5.QtWidgets import (QApplication,QDialog,QGridLayout,QLCDNumber,
QLabel,QSizePolicy,QSpinBox,QWidget)


# 这段代码貌似与界面x11有关 暂时看不明白要干嘛
X11=hasattr(PyQt5.QtGui,"qt_x11_wait_for_window_manager")
X11 = True

try:
from PyQt5.QtGui import qt_x11_wait_for_window_manager
except ImportError:
X11 = False


class FractionSlider(QWidget):
#定义边距
XMARGIN=12
YMARGIN=9
WSTRING='999'

#定义一个valueChanged的信号
# valueChanged=signal().valueChanged
valueChanged=pyqtSignal(int)

#定义初始化函数
def __init__(self,numerator=0,denominator=10,parent=None):
super(FractionSlider,self).__init__(parent)
self.__numerator=numerator
self.__denominator=denominator
#可以通过滚轮接受焦点
self.setFocusPolicy(Qt.WheelFocus)
#可以伸展和收缩,不过sizeHint() 的返回值规定了
# widget 能缩小到的最小尺寸,同时它比 Preferred 更具优势去获取额外空间
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 sizeHint(self):
return self.minimumSizeHint()

#计算整个界面完全显示的最小的大小
def minimumSizeHint(self):
font=QFont(self.font())
font.setPointSize(font.pointSize()-1)
#计算对应该字体的字符的大小
fm=QFontMetricsF(font)
#返回大小存疑
return QSize(fm.width(FractionSlider.WSTRING)*self.__denominator,
fm.height()*4+FractionSlider.YMARGIN)

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')
#更新widget绘制
self.update()
self.updateGeometry()

#鼠标按下的事件
def mousePressEvent(self, event):
if event.button()==Qt.LeftButton:
self.moveSlider(event.x())
event.accept()
else:
#不是左键的话就执行默认的事件 其实这段代码可以不写就是将型信号传递给上级
QWidget.mousePressEvent(self,event)

def mouseMoveEvent(self,event):
#event.x() 是不是指的是widget内部的坐标
self.moveSlider(event.x())

def moveSlider(self,x):
span=self.width()-2*FractionSlider.XMARGIN
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
#发射值改变的信号
#emit参数可以附带参数信息
self.valueChanged.emit(self.__numerator)

#重新绘制图形
self.update()

#根据按下的按键的不同确定change的变化程度
def KeyPressEvent(self, event: QKeyEvent):
change=0
key=None
if key==Qt.Key_Home:
change=-self.__denominator
elif key in (Qt.Key_Up,Qt.Key_Right):
change=1
elif key in (Qt.Key_Down,Qt.Key_Right):
change=-1
elif key==Qt.Key_PageDown:
change=-(self.__denominator//10+1)
elif key==Qt.Key_PageUp:
change=self.__denominator//10+1
elif key==Qt.Key_End:
change=self.__denominator
if change:
numerator=self.__numerator+change
numerator=max(0,min(numerator,self.__denominator))
if numerator!=self.__numerator:
self.__numerator=numerator
self.valueChanged.emit(self.__numerator)
self.update()
event.accept()
else:
QWidget.keyPressEvent(self,event)

def paintEvent(self, event=None):
font=QFont(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()-self.XMARGIN*2
value=self.decimal()
painter=QPainter(self)
#设置反锯齿
painter.setRenderHint(QPainter.Antialiasing)
painter.setRenderHint(QPainter.TextAntialiasing)
#QPalette参见page337
#pen用于绘制文字和轮廓 调色板设定颜色
painter.setPen(self.palette().color(QPalette.Mid))
#brush负责填充背景
painter.setBrush(self.palette().brush(QPalette.AlternateBase))
painter.drawRect(self.rect())

#绘制了一个进度条区域方便指针在上面滑动
segColor=QColor(Qt.green).darker(120)
#darker函数默认参数是200
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
ymargin=FractionSlider.YMARGIN
yOffeset=segHeight+fm.height()

for i in range(self.__denominator+1):
painter.setPen(segLineColor)
#draw from pos1 to pos2
painter.drawLine(x,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=yOffeset
rect.moveCenter(QPointF(x,y+fm.height()/2.0))
painter.drawText(rect,Qt.AlignCenter,str(self.__denominator))
#绘制分割线 indent是半个字宽
painter.drawLine(QPointF(rect.left()+indent,y),QPointF(rect.right()-indent,y))

x+=segWidth

#绘制三角形刻度标记图标
#其实这段代码写的是有问题的 只有 XMARGIN = Span/(denominator-1)时 才能这么写
span=int(span)
y=FractionSlider.YMARGIN-0.5
# triangle=[QPointF(value*span,y),
# QPointF(value*span+2*FractionSlider.XMARGIN,y),
# QPointF(value*span+FractionSlider.XMARGIN,y+fm.height())]

#确定三角形中间那个点的位置
x=FractionSlider.XMARGIN+value*span
# 确定三角形上边长度/2的长度
trianglewidth=segWidth-indent
#找出围的三角形的点
triangle=[QPointF(x-trianglewidth,y),QPointF(x+trianglewidth,y),QPointF(x,y+fm.height())]
painter.setPen(Qt.yellow)
painter.setBrush(Qt.darkYellow)
painter.drawPolygon(QPolygonF(triangle))


if __name__=='__main__':
import sys
app=QApplication(sys.argv)
form=QDialog()

sliderLabel=QLabel('&Fraction')
slider=FractionSlider(denominator=12)
sliderLabel.setBuddy(slider)

denominatorLabel=QLabel('&Denominator')
denominatorSpinBox=QSpinBox()
denominatorSpinBox.setRange(3,30)
denominatorSpinBox.setValue(slider.fraction()[1])
denominatorSpinBox.setAlignment(Qt.AlignRight|Qt.AlignCenter)
denominatorLabel.setBuddy(denominatorSpinBox)

numeratorLabel=QLabel('&Numberator')
numeratorLCD=QLCDNumber()
numeratorLCD.setSegmentStyle(QLCDNumber.Flat)
numeratorLabel.setBuddy(numeratorLCD)


#网格布局
layout=QGridLayout()
layout.addWidget(sliderLabel,0,0)
layout.addWidget(slider,0,1,1,5)
layout.addWidget(numeratorLabel,1,0)
layout.addWidget(numeratorLCD,1,1)
layout.addWidget(denominatorLabel,1,2)
layout.addWidget(denominatorSpinBox,1,3)
form.setLayout(layout)

def valueChanged(denominitor):
numerator=slider.decimal()*denominitor
slider.setFraction(numerator,denominitor)
numeratorLCD.display(numerator)

def dsp(num):
numeratorLCD.display(num)
#分母发生变化
denominatorSpinBox.valueChanged.connect(valueChanged)
#分子发生变化
slider.valueChanged.connect(dsp)

form.setWindowTitle('Fraction Slider')
form.show()
app.exec_()