PyQt5 布局管理、事件、信号以及对话框

布局管理

绝对定位

每个程序都是以像素为单位区分元素的位置,衡量元素的⼤⼩。所以我们完全可以使⽤绝对定位搞定每个元素和窗⼜的位置。

局限性:

  • 元素不会随着我们更改窗⼜的位置和⼤⼩⽽变化
  • 不能适⽤于不同的平台和不同分辨率的显⽰器
  • 更改应⽤字体⼤⼩会破坏布局
  • 如果我们决定重构这个应⽤,需要全部计算⼀下每个元素的位置和⼤⼩
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
	    # 使⽤move()⽅法定位了每⼀个元素,使⽤x、y坐标。x、y坐标的原点是程序的左上⾓
        lbl1 = QLabel('Zetcode', self)
        lbl1.move(15, 10)
        lbl2 = QLabel('tutorials', self)
        lbl2.move(35, 40)
        lbl3 = QLabel('for programmers', self)
        lbl3.move(55, 70)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Absolute')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在这里插入图片描述

盒布局

使⽤盒布局能让程序具有更强的适应性。这个才是布局⼀个应⽤的更合适的⽅式。 QHBoxLayout 和QVBoxLayout 是基本的布局类,分别是⽔平布局和垂直布局。

import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QVBoxLayout, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
     	# 创建按钮
        okButton = QPushButton("OK")
        cancelButton = QPushButton("Cancel")
        # 创建⼀个⽔平布局,增加两个按钮和弹性空间
        hbox = QHBoxLayout()
        # stretch函数在两个按钮前⾯增加了⼀些弹性空间
        hbox.addStretch(1)
        hbox.addWidget(okButton)
        hbox.addWidget(cancelButton)
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        # 把这个⽔平布局放到⼀个垂直布局盒⾥⾯
        # 弹性元素会把所有的元素⼀起都放置在应⽤的右下⾓
        self.setLayout(vbox)
        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('Buttons')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())


在这里插入图片描述

栅格布局

最常⽤的还是栅格布局了。这种布局是把窗⼜分为⾏和列。创建和使⽤栅格布局,需要使⽤QGridLayout模块。

import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QGridLayout, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
    	# 创建栅格化的按钮
        grid = QGridLayout()
        self.setLayout(grid)
        # 创建⼀个QGridLayout实例,并把放到程序窗口⾥
        names = ['Cls', 'Bck', '', 'Close',
                 '7', '8', '9', '/',
                 '4', '5', '6', '*',
                 '1', '2', '3', '-',
                 '0', '.', '=', '+']
        # 将要使⽤的按钮的名称
        positions = [(i, j) for i in range(5) for j in range(4)]
        # 创建按钮位置列表
        for position, name in zip(positions, names):
            if name == '':
                continue
            button = QPushButton(name)
            # 创建按钮,并使⽤ addWidget() ⽅法把按钮放到布局⾥⾯
            grid.addWidget(button, *position)
            self.move(300, 150)
            self.setWindowTitle('Calculator')
            self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在这里插入图片描述

制作提交反馈信息的布局

组件能跨列和跨⾏展⽰

import sys
from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit, QGridLayout,QTextEdit, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        title = QLabel('Title')
        author = QLabel('Author')
        review = QLabel('Review')
        titleEdit = QLineEdit()  # QLineEdit 只有⼀⾏
        authorEdit = QLineEdit()
        reviewEdit = QTextEdit()  # QTextEdit 不⽌⼀⾏
        grid = QGridLayout()
        grid.setSpacing(10)  # 珊格间隙的像素值,单位是像素
        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)
        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)
        grid.addWidget(review, 3, 0)
        # 指定这个元素跨5⾏显⽰
        grid.addWidget(reviewEdit, 3, 1, 5, 1)
        self.setLayout(grid)
        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Review')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在这里插入图片描述

事件和信号

事件

所有的应⽤都是事件驱动的

在事件模型中,有三个⾓⾊:

  • 事件源 — 是发⽣了状态改变的对象
  • 事件 — 是这个对象状态改变的内容
  • 事件⽬标 —事件想作⽤的⽬标

事件源绑定事件处理函数,然后作⽤于事件⽬标⾝上。

PyQt5处理事件⽅⾯有个signal and slot机制。Signals and slots⽤于对象间的通讯。事件触发的时候,发⽣⼀个signal,slot是⽤来被Python调⽤的slot只有在事件触发的时候才能调⽤。

Signals & slots

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QLCDNumber, QSlider, QVBoxLayout, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        lcd = QLCDNumber(self)
        sld = QSlider(Qt.Horizontal, self)
        vbox = QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(sld)
        self.setLayout(vbox)
        # 把滑块的变化和数字的变化绑定在⼀起
        sld.valueChanged.connect(lcd.display)
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Signal and slot')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

在这里插入图片描述

重构事件处理器

在PyQt5中,事件处理器经常被重写。
替换事件处理器函数 keyPressEvent() ,实现按下ESC键程序就会退出。

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Event handler')
        self.show()

    def keyPressEvent(self, e):  # 键盘按下事件
        if e.key() == Qt.Key_Escape:  # 按下ESC键
            self.close()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

事件对象

事件对象是⽤python来描述⼀系列的事件⾃⾝属性的对象

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QLabel

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        grid = QGridLayout()
        grid.setSpacing(10)
        x = 0
        y = 0
        # 显⽰⿏标的X和Y坐标
        self.text = "x: {0}, y: {1}".format(x, y)
        self.label = QLabel(self.text, self)
        grid.addWidget(self.label, 0, 0, Qt.AlignTop)  # Qt.AlignTop 居顶
        self.setMouseTracking(True)
        self.setLayout(grid)
        self.setGeometry(300, 300, 350, 200)
        self.setWindowTitle('Event object')
        self.show()

    def mouseMoveEvent(self, e):
        # 事件追踪默认没有开启,当开启后才会追踪⿏标的点击事件
        # e 代表了事件对象,x() 和 y() ⽅法得到⿏标的x和y
        # 坐标点,然后拼成字符串输出到 QLabel 组件⾥
        x = e.x()
        y = e.y()
        text = "x: {0}, y: {1}".format(x, y)
        self.label.setText(text)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

X和Y的值代表当前鼠标的位置
在这里插入图片描述

事件发送

import sys
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        btn1 = QPushButton("Button 1", self)
        btn1.move(30, 50)
        btn2 = QPushButton("Button 2", self)
        btn2.move(150, 50)
        # 两个按钮都和同⼀个slot绑定
        btn1.clicked.connect(self.buttonClicked)
        btn2.clicked.connect(self.buttonClicked)
        self.statusBar()
        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Event sender')
        self.show()
	
	# buttonClicked() ⽅法决定了是哪个按钮能调⽤ sender() ⽅法
    def buttonClicked(self):
        sender = self.sender()
        self.statusBar().showMessage(sender.text() + ' was pressed')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

⽤调⽤ sender() ⽅法的⽅式决定了事件源。状态栏显⽰了被点击的按钮
在这里插入图片描述

信号发送

QObject 实例能发送事件信号

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication

class Communicate(QObject):
    # 创建⼀个叫closeApp的信号,这个信号会在⿏标按下的时候触发,事件与 QMainWindow 绑定
    # 创建⼀个 pyqtSignal() 属性的信号
    closeApp = pyqtSignal()

class Example(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.c = Communicate()
        # closeApp 信号 QMainWindow 的 close() ⽅法绑定
        self.c.closeApp.connect(self.close)
        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Emit signal')
        self.show()

    def mousePressEvent(self, event):
        # 点击窗⼜的时候,发送closeApp信号,程序终⽌
        self.c.closeApp.emit()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

点击窗口对话框便会消失,不展示

对话框

输⼊⽂字

QInputDialog 提供了⼀个简单⽅便的对话框,可以输⼊字符串,数字或列表

from PyQt5.QtWidgets import QWidget, QPushButton, QLineEdit,  QInputDialog, QApplication
import sys

class Example(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.btn = QPushButton('Dialog', self)
        self.btn.move(20, 20)
        self.btn.clicked.connect(self.showDialog)
        self.le = QLineEdit(self)
        self.le.move(130, 22)
        self.setGeometry(300, 300, 290, 150)
        self.setWindowTitle('Input dialog')
        self.show()

    def showDialog(self):
        # 第⼀个参数是输⼊框的标题,第⼆个参数是输⼊框的占位符。对话框
        # 返回输⼊内容和⼀个布尔值,如果点击的是OK按钮,布尔值就返回True
        text, ok = QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
        if ok:
            self.le.setText(str(text))

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_()

该⽰例生成⼀个按钮和⼀个输⼊框,可以直接在输入框输入内容
在这里插入图片描述
也可以点击按钮(Dialog)弹出对话框,输入。点击ok后,内容会替换输入框的内容
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值