专栏首页Python编程 pyqt matplotlibPyQt 自定义窗口部件(二)

PyQt 自定义窗口部件(二)

本篇介绍用如何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编程 pyqt matplotlib(wsplovePython),作者:wsp

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-05-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PyQt5 非模态对话框(apply 型)

    如果希望用户可以重复更改对话框中的参数并能马上看到修改结果,那么就要使用非模态对话框,这样用户就可以按照他们喜欢的方式来持续不断地修改数据并验证修改的结果了。

    用户6021899
  • 有限元一阶四面体单元python编程(二)

    用户6021899
  • 有限元平面四边形等差单元python编程

    四边形等参单元的刚度矩阵是二重积分式,我想用Maple求解析解,算了很久也没有算出结果。所有我的编程思路是先用 sympy 求出 单元刚度矩阵的符号解,再用la...

    用户6021899
  • 小甲鱼《零基础学习Python》课后笔记(三十七):类和对象——面向对象编程

    1.当程序员不想把同一段代码写几次,他们发明了函数解决了这种情况。当程序员已经有了一个类,而又想建立一个非常接近的新类,他们会怎么做呢? 定义一个新类继承已有...

    小火柴棒
  • 学习笔记CB012: LSTM 简单实现、完整实现、torch、小说训练word2vec lstm机器人

    LSTM(Long Short Tem Memory)特殊递归神经网络,神经元保存历史记忆,解决自然语言处理统计方法只能考虑最近n个词语而忽略更久前词语的问题。...

    利炳根
  • python笔记:随机数,md5,en/decoder

    超级大猪
  • Simplex 单纯形算法的python

    算法可以在给定一个包含线性规划问题的标准形式的描述下,求解该线性规划问题。 例如某一个 pro.txt 文件内容如下:

    py3study
  • 庆祝法国队夺冠:用Python放一场烟花秀

    天天敲代码的朋友,有没有想过代码也可以变得很酷炫又浪漫?今天就教大家用Python模拟出绽放的烟花庆祝昨晚法国队夺冠,工作之余也可以随时让程序为自己放一场烟花秀...

    猫咪编程
  • 21天打造分布式爬虫-Selenium爬取拉钩职位信息(六)

    zhang_derek
  • python3-使用requests模拟

    py3study

扫码关注云+社区

领取腾讯云代金券