本文转载自:http://code.py40.com/pyqt5/

一、PyQt5基本功能

简单的例子

PyQt5是一种高级的语言,下面只有几行代码就能显示一个小窗口。底层已经实现了窗口的基本功能。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40.com PyQt5 tutorial In this example, we create a simple
window in PyQt5. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys #这里我们提供必要的引用。基本控件位于pyqt5.qtwidgets模块中。
from PyQt5.QtWidgets import QApplication, QWidget if __name__ == '__main__':
#每一pyqt5应用程序必须创建一个应用程序对象。sys.argv参数是一个列表,从命令行输入参数。
app = QApplication(sys.argv)
#QWidget部件是pyqt5所有用户界面对象的基类。他为QWidget提供默认构造函数。默认构造函数没有父类。
w = QWidget()
#resize()方法调整窗口的大小。这离是250px宽150px高
w.resize(250, 150)
#move()方法移动窗口在屏幕上的位置到x = 300,y = 300坐标。
w.move(300, 300)
#设置窗口的标题
w.setWindowTitle('Simple')
#显示在屏幕上
w.show() #系统exit()方法确保应用程序干净的退出
#的exec_()方法有下划线。因为执行是一个Python关键词。因此,exec_()代替
sys.exit(app.exec_())

上面的示例代码在屏幕上显示一个小窗口。

应用程序的图标

应用程序图标是一个小的图像,通常在标题栏的左上角显示。在下面的例子中我们将介绍如何做pyqt5的图标。同时我们也将介绍一些新方法。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
py40 PyQt5 tutorial This example shows an icon
in the titlebar of the window. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QIcon class Example(QWidget): def __init__(self):
super().__init__() self.initUI() #界面绘制交给InitUi方法 def initUI(self):
#设置窗口的位置和大小
self.setGeometry(300, 300, 300, 220)
#设置窗口的标题
self.setWindowTitle('Icon')
#设置窗口的图标,引用当前目录下的web.png图片
self.setWindowIcon(QIcon('web.png')) #显示窗口
self.show() if __name__ == '__main__':
#创建应用程序和对象
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

前面的例子是在程序风格。Python编程语言支持程序和面向对象编程风格。Pyqt5使用OOP编程。

class Example(QWidget):

    def __init__(self):
super().__init__()
...

面向对象编程有三个重要的方面:类、变量和方法。这里我们创建一个新的类为Examle。Example继承自QWidget类。

显示提示语

在下面的例子中我们显示一个提示语

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This example shows a tooltip on
a window and a button. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import (QWidget, QToolTip,
QPushButton, QApplication)
from PyQt5.QtGui import QFont class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self):
#这种静态的方法设置一个用于显示工具提示的字体。我们使用10px滑体字体。
QToolTip.setFont(QFont('SansSerif', 10)) #创建一个提示,我们称之为settooltip()方法。我们可以使用丰富的文本格式
self.setToolTip('This is a <b>QWidget</b> widget') #创建一个PushButton并为他设置一个tooltip
btn = QPushButton('Button', self)
btn.setToolTip('This is a <b>QPushButton</b> widget') #btn.sizeHint()显示默认尺寸
btn.resize(btn.sizeHint()) #移动窗口的位置
btn.move(50, 50) self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Tooltips')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

运行程序,显示一个窗口

关闭窗口

关闭一个窗口可以点击标题栏上的X。在下面的例子中,我们将展示我们如何通过编程来关闭窗口。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program creates a quit
button. When we press the button,
the application terminates. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QWidget, QPushButton, QApplication
from PyQt5.QtCore import QCoreApplication class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): qbtn = QPushButton('Quit', self)
qbtn.clicked.connect(QCoreApplication.instance().quit)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50) self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Quit button')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

消息框

默认情况下,如果我们单击x按钮窗口就关门了。有时我们想修改这个默认的行为。例如我们在编辑器中修改了一个文件,当关闭他的时候,我们显示一个消息框确认。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
ZetCode PyQt5 tutorial This program shows a confirmation
message box when we click on the close
button of the application window. author: Jan Bodnar
website: zetcode.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QWidget, QMessageBox, QApplication class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Message box')
self.show() def closeEvent(self, event): reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

我们关闭窗口的时候,触发了QCloseEvent。我们需要重写closeEvent()事件处理程序。

reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)

我们显示一个消息框,两个按钮:“是”和“不是”。第一个字符串出现在titlebar。第二个字符串消息对话框中显示的文本。第三个参数指定按钮的组合出现在对话框中。最后一个参数是默认按钮,这个是默认的按钮焦点。

if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
event.ignore()

我们处理返回值,如果单击Yes按钮,关闭小部件并终止应用程序。否则我们忽略关闭事件。

窗口显示在屏幕的中间

下面的脚本显示了如何在屏幕中心显示窗口。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program centers a window
on the screen. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QWidget, QDesktopWidget, QApplication class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): self.resize(250, 150)
self.center() self.setWindowTitle('Center')
self.show() #控制窗口显示在屏幕中心的方法
def center(self): #获得窗口
qr = self.frameGeometry()
#获得屏幕中心点
cp = QDesktopWidget().availableGeometry().center()
#显示到屏幕中心
qr.moveCenter(cp)
self.move(qr.topLeft()) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

QtGui,QDesktopWidget类提供了用户的桌面信息,包括屏幕大小。

二、PyQt5布局管理

PyQt5布局有两种方式,绝对定位和布局类

绝对定位

程序指定每个控件的位置和大小(以像素为单位)。

绝对定位有以下限制:

  • 如果我们调整窗口,控件的大小和位置不会改变
  • 在各种平台上应用程序看起来会不一样
  • 如果改变字体,我们的应用程序的布局就会改变
  • 如果我们决定改变我们的布局,我们必须完全重做我们的布局

下面的例子显示了一个绝对定位

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This example shows three labels on a window
using absolute positioning. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): 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_())

我们使用move()方法来控制控件的位置。

框布局 Boxlayout

我们使用QHBoxLayout和QVBoxLayout,来分别创建横向布局和纵向布局。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial In this example, we position two push
buttons in the bottom-right corner
of the window. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" 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()
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_())

在这个例子中,我们使用HBoxLayout和QVBoxLayout并添加伸展因子,在窗口的右下角显示两个按钮。

hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)

我们创建一个水平布局和添加一个伸展因子和两个按钮。两个按钮前的伸展增加了一个可伸缩的空间。这将推动他们靠右显示。

vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)

创建一个垂直布局,并添加伸展因子,让水平布局显示在窗口底部

self.setLayout(vbox)

最后,我们设置窗口的布局界面

表格布局 QGridLayout

表格布局将空间划分为行和列。我们使用QGridLayout类创建一个网格布局。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial In this example, we create a skeleton
of a calculator using a QGridLayout. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout,
QPushButton, QApplication) class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): grid = QGridLayout()
self.setLayout(grid) 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)
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_())

在我们的示例中,我们创建一个网格的按钮。

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)
grid.addWidget(button, *position)

创建按钮并使用addWidget()方法添加到布局中。

评论的例子

控件可以在网格中跨越多个行或列。在下一个示例中,我们说明了这一点。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial In this example, we create a bit
more complicated window layout using
the QGridLayout manager. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, 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()
authorEdit = QLineEdit()
reviewEdit = 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)
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_())

我们创建一个窗口,其中有三个标签,两个行编辑和一个文本编辑窗口小控件。然后使用QGridLayout完成布局。

grid = QGridLayout()
grid.setSpacing(10)

创建一个网格布局和设置组件之间的间距。

grid.addWidget(reviewEdit, 3, 1, 5, 1)

在添加一个小的控件到网格的时候,我们可以提供小部件的行和列跨。在例子中,reviewEdit控件跨度5行。

三、PyQt5菜单和工具栏

主窗口

QMainWindow 类提供了一个主要的应用程序窗口。你用它可以让应用程序添加状态栏,工具栏和菜单栏。

状态栏

状态栏用于显示状态信息。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program creates a statusbar. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QMainWindow, QApplication class Example(QMainWindow): def __init__(self):
super().__init__() self.initUI() def initUI(self): self.statusBar().showMessage('Ready') self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Statusbar')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

你用QMainWindow创建状态栏的小窗口。

self.statusBar().showMessage('Ready')

QMainWindow类第一次调用statusBar()方法创建一个状态栏。后续调用返回的状态栏对象。showMessage()状态栏上显示一条消息。

菜单栏

菜单栏是常见的窗口应用程序的一部分。(Mac OS将菜单条不同。得到类似的结果,我们可以添加以下行:menubar.setNativeMenuBar(假)。)

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program creates a menubar. The
menubar has one menu with an exit action. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from PyQt5.QtGui import QIcon class Example(QMainWindow): def __init__(self):
super().__init__() self.initUI() def initUI(self): exitAction = QAction(QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(qApp.quit) self.statusBar() #创建一个菜单栏
menubar = self.menuBar()
#添加菜单
fileMenu = menubar.addMenu('&File')
#添加事件
fileMenu.addAction(exitAction) self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Menubar')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在上面的例子中,我们创建一个菜单栏和一个菜单。这个菜单将终止应用程序。Ctrl + Q的行动是可访问的快捷方式。

exitAction = QAction(QIcon('exit.png'), '&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')

QAction可以操作菜单栏,工具栏,或自定义键盘快捷键。上面三行,我们创建一个事件和一个特定的图标和一个“退出”的标签。然后,在定义该操作的快捷键。
第三行创建一个鼠标指针悬停在该菜单项上时的提示。

exitAction.triggered.connect(qApp.quit)

当我们点击菜单的时候,调用qApp.quit,终止应用程序。

工具栏

工具栏提供了一个快速访问的入口。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program creates a toolbar.
The toolbar has one action, which
terminates the application, if triggered. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from PyQt5.QtGui import QIcon class Example(QMainWindow): def __init__(self):
super().__init__() self.initUI() def initUI(self): exitAction = QAction(QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(qApp.quit) self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(exitAction) self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('Toolbar')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在上面的例子中,我们创建一个简单的工具栏。工具栏有有一个按钮,点击关闭窗口。

exitAction = QAction(QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(qApp.quit)

类似于上面的菜单栏的例子,我们创建一个QAction事件。该事件有一个标签、图标和快捷键。退出窗口的方法

把他们放在一起

在本节的最后一个例子中,我们将创建一个菜单条,工具栏和状态栏的小窗口

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial This program creates a skeleton of
a classic GUI application with a menubar,
toolbar, statusbar, and a central widget. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" import sys
from PyQt5.QtWidgets import QMainWindow, QTextEdit, QAction, QApplication
from PyQt5.QtGui import QIcon class Example(QMainWindow): def __init__(self):
super().__init__() self.initUI() def initUI(self): textEdit = QTextEdit()
self.setCentralWidget(textEdit) exitAction = QAction(QIcon('exit24.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close) self.statusBar() menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAction) toolbar = self.addToolBar('Exit')
toolbar.addAction(exitAction) self.setGeometry(300, 300, 350, 250)
self.setWindowTitle('Main window')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

创建了一个窗口

textEdit = QTextEdit()
self.setCentralWidget(textEdit)

我们创建了一个QTextEdit,并把他设置为窗口的布局

四、PyQt5事件和信号

事件 Event

所有的GUI程序都是事件驱动的。事件主要由用户触发,但也可能有其他触发方式:例如网络连接、window manager或定时器。当我们调用QApplication的exec_()方法时会使程序进入主循环。主循环会获取并分发事件。

在事件模型中,有三个参与者:

  • 事件源
  • 事件对象
  • 事件接收者

事件源是状态发生变化的对象。它会生成事件。事件(对象)封装了事件源中状态的变动。事件接收者是要通知的对象。事件源对象将事件处理的工作交给事件接收者。

PyQt5有一个独特的signal&slot(信号槽)机制来处理事件。信号槽用于对象间的通信。signal在某一特定事件发生时被触发,slot可以是任何callable对象。当signal触发时会调用与之相连的slot。

信号槽 Signals & slots

这是一个使用信号槽的PyQt5例子。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
Py40 PyQt5 tutorial In this example, we connect a signal
of a QSlider to a slot of a QLCDNumber. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" 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 & slot')
self.show() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

这个例子中展示了一个QtGui.QLCDNumber和QtGui.QSlider。lcd的值会随着滑块的拖动而改变。

sld.valueChanged.connect(lcd.display)

在这里我们将滚动条的valueChanged信号连接到lcd的display插槽。

sender是发出信号的对象。receiver是接收信号的对象。slot(插槽)是对信号做出反应的方法。

重新实现事件处理器

在PyQt5中常通过重新实现事件处理器来处理事件。

#!/usr/bin/python3
# -*- coding: utf-8 -*- """
pyu40 PyQt5 tutorial In this example, we reimplement an
event handler. author: Jan Bodnar
website: py40.com
last edited: January 2015
""" 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:
self.close() if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在示例中我们重新实现了keyPressEvent()事件处理器。

def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.close()

我们按下Escape键会使程序退出。

事件发送者

有时需要知道信号是由哪个控件发出的。对此PyQt5提供了sender()方法。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
""" 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) btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked) self.statusBar() self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Event sender')
self.show() 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_())

我们创建了两个按钮。在buttonClicked()方法中通过调用sender()方法来判断当前按下的是哪个按钮。

btn1.clicked.connect(self.buttonClicked)
btn2.clicked.connect(self.buttonClicked)

两个按钮连接到了同一个插槽。

def buttonClicked(self):

    sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')

我们通过调用sender()方法来判断信号源, 并将其名称显示在窗体的状态栏中。

五、PyQt5对话框

对话框窗口或对话框是现代GUI应用程序最不可或缺的一部分。一个对话框被定义为两个或两个以上的人之间的谈话。在计算机应用程序对话框窗口用于“交谈”应用程序。一个对话框用于输入数据,修改数据,更改应用程序设置等。

QInputDialog

QInputDialog提供了一种简单方便的对话框从用户得到一个值。输入值可以是字符串,一个数字,或一个项目从一个列表。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
""" import sys
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit,
QInputDialog, QApplication) 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): 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_())

这个例子显示一个按钮和一个文本框,用户点击按钮显示一个输入框,用户输入信息会显示在文本框中。

text, ok = QInputDialog.getText(self, 'Input Dialog',
'Enter your name:')

这行代码显示输入对话框。第一个字符串是一个对话框标题,第二个是对话框中的消息。对话框返回输入的文本和一个布尔值。点击Ok按钮,布尔值是True。

if ok:
self.le.setText(str(text))

对话框收到的文本消息会显示在文本框中

QColorDialog

QColorDialog显示一个用于选择颜色值的对话框。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
""" import sys
from PyQt5.QtWidgets import (QWidget, QPushButton, QFrame,
QColorDialog, QApplication)
from PyQt5.QtGui import QColor class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): col = QColor(0, 0, 0) self.btn = QPushButton('Dialog', self)
self.btn.move(20, 20) self.btn.clicked.connect(self.showDialog) self.frm = QFrame(self)
self.frm.setStyleSheet("QWidget { background-color: %s }"
% col.name())
self.frm.setGeometry(130, 22, 100, 100) self.setGeometry(300, 300, 250, 180)
self.setWindowTitle('Color dialog')
self.show() def showDialog(self): col = QColorDialog.getColor() if col.isValid():
self.frm.setStyleSheet("QWidget { background-color: %s }"
% col.name()) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

这个应用程序显示一个按钮和一个QFrame。QFrame的背景为黑色。通过QColorDialog,我们可以改变它的背景。

col = QColor(0, 0, 0) 

初始化QFrame的颜色为黑色

col = QColorDialog.getColor()

这一行代码弹出QColorDialog

if col.isValid():
self.frm.setStyleSheet("QWidget { background-color: %s }"
% col.name())

我们要先检查col的值。如果点击的是Cancel按钮,返回的颜色值是无效的。当颜色值有效时,我们通过样式表(style sheet)来改变背景颜色。

QFontDialog

QFontDialog对话框用以选择字体

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QPushButton,
QSizePolicy, QLabel, QFontDialog, QApplication) class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): vbox = QVBoxLayout() btn = QPushButton('Dialog', self)
btn.setSizePolicy(QSizePolicy.Fixed,
QSizePolicy.Fixed) btn.move(20, 20) vbox.addWidget(btn) btn.clicked.connect(self.showDialog) self.lbl = QLabel('Knowledge only matters', self)
self.lbl.move(130, 20) vbox.addWidget(self.lbl)
self.setLayout(vbox) self.setGeometry(300, 300, 250, 180)
self.setWindowTitle('Font dialog')
self.show() def showDialog(self): font, ok = QFontDialog.getFont()
if ok:
self.lbl.setFont(font) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在这个例子中,我们创建了一个按钮和一个标签,通过QFontDialog来改变标签的字体

font, ok = QFontDialog.getFont()

这一行代码弹出字体选择对话框,getFont()方法返回字体名称和ok参数,如果用户点击了ok他就是True,否则就是false

if ok:
self.label.setFont(font)

如果我们点击了ok,标签的字体就会被改变

QFileDialog

QFileDialog是一个让用户选择文件和目录的对话框,可用以选择打开或保存文件

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QMainWindow, QTextEdit,
QAction, QFileDialog, QApplication)
from PyQt5.QtGui import QIcon class Example(QMainWindow): def __init__(self):
super().__init__() self.initUI() def initUI(self): self.textEdit = QTextEdit()
self.setCentralWidget(self.textEdit)
self.statusBar() openFile = QAction(QIcon('open.png'), 'Open', self)
openFile.setShortcut('Ctrl+O')
openFile.setStatusTip('Open new File')
openFile.triggered.connect(self.showDialog) menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(openFile) self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('File dialog')
self.show() def showDialog(self): fname = QFileDialog.getOpenFileName(self, 'Open file', '/home') if fname[0]:
f = open(fname[0], 'r') with f:
data = f.read()
self.textEdit.setText(data) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

这个例子展示了一个菜单栏,中部TextEdit控件和一个状态栏。菜单项Open会显示用于选择文件的QtGui.QFileDialog对话框。选定文件的内容会加载到TextEdit控件中。

class Example(QMainWindow):

    def __init__(self):
super().__init__() self.initUI()

示例窗体继承自QMainWindow,因为我们要将TextEdit控件置于窗体中央。

fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')

弹出QFileDialog对话框,第一个字符串参数是对话框的标题,第二个指定对话框的工作目录,默认情况下文件筛选器会匹配所有类型的文件(*)

if fname[0]:
f = open(fname[0], 'r') with f:
data = f.read()
self.textEdit.setText(data)

读取了选择的文件并将文件内容显示到了TextEdit控件。

六、PyQt5控件

控件是构建应用程序的基本模块,PyQt5提供各种各样的控件,包括按钮、复选框、滑动条、列表框等。在这部分的教程中,我们将详细介绍几个常用的控件:QCheckBox,ToggleButton,QSlider,QProgressBar,QCalendarWidget。

QCheckBox

QCheckBox复选框控件,它有两个状态:打开和关闭,他是一个带有文本标签(Label)的控件。复选框常用于表示程序中可以启用或禁用的功能。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we determine the event sender
object. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QWidget, QCheckBox, QApplication
from PyQt5.QtCore import Qt class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): cb = QCheckBox('Show title', self)
cb.move(20, 20)
cb.toggle()
cb.stateChanged.connect(self.changeTitle) self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('QCheckBox')
self.show() def changeTitle(self, state): if state == Qt.Checked:
self.setWindowTitle('QCheckBox')
else:
self.setWindowTitle('') if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在我们的示例中,我们将创建一个复选框,将切换窗口标题。

cb = QCheckBox('Show title', self)

这是QCheckBox的构造行数

cb.toggle()

我们有设置窗口标题,所以我们也必须检查复选框。默认情况下,没有设置窗口标题和也没有勾选复选框。

cb.stateChanged.connect(self.changeTitle)

我们将自定义的changeTitle()方法连接到stateChanged信号。这个方法会切换窗体的标题。

def changeTitle(self, state):

    if state == Qt.Checked:
self.setWindowTitle('QCheckBox')
else:
self.setWindowTitle('')

复选框的状态经由state参数传入changeTitle()方法。在勾选复选框时设置窗体标题,取消勾选时就将标题设为空字符串。

开关按钮 Toggle button

ToggleButton是QPushButton的一种特殊模式。它是一个有两种状态的按钮:按下与未按下。通过点击在这两种状态间来回切换。这种功能在某些场景会很实用。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we create three toggle buttons.
They will control the background color of a
QFrame. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QPushButton,
QFrame, QApplication)
from PyQt5.QtGui import QColor class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): self.col = QColor(0, 0, 0) redb = QPushButton('Red', self)
redb.setCheckable(True)
redb.move(10, 10) redb.clicked[bool].connect(self.setColor) greenb = QPushButton('Green', self)
greenb.setCheckable(True)
greenb.move(10, 60) greenb.clicked[bool].connect(self.setColor) blueb = QPushButton('Blue', self)
blueb.setCheckable(True)
blueb.move(10, 110) blueb.clicked[bool].connect(self.setColor) self.square = QFrame(self)
self.square.setGeometry(150, 20, 100, 100)
self.square.setStyleSheet("QWidget { background-color: %s }" %
self.col.name()) self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Toggle button')
self.show() def setColor(self, pressed): source = self.sender() if pressed:
val = 255
else: val = 0 if source.text() == "Red":
self.col.setRed(val)
elif source.text() == "Green":
self.col.setGreen(val)
else:
self.col.setBlue(val) self.square.setStyleSheet("QFrame { background-color: %s }" %
self.col.name()) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

代码中我们创建了三个ToggleButton与一个QWidget。我们将QWidget的背景色设为黑色。ToggleButton会切换颜色值中的红色、绿色与蓝色部分。QWidget的背景颜色依赖于按下的按钮。

self.col = QColor(0, 0, 0)

这是初始黑颜色的值。

redb = QPushButton('Red', self)
redb.setCheckable(True)
redb.move(10, 10)

我们创建一个QPushButton并通过其setCheckable()方法来得到一个ToggleButton。

redb.clicked[bool].connect(self.setColor)

将clicked信号连接到用户自定义的方法。我们通过clicked信号操作一个布尔值。

if source.text() == "Red":
self.col.setRed(val)

我们得到了按下的按钮。

if source.text() == "Red":
self.col.setRed(val)

如果按下的是red按钮,我们要相应地更新颜色中的红包部分。

self.square.setStyleSheet("QFrame { background-color: %s }" %
self.col.name())

滑动条 QSlider

QSlider是一个带有简单滑块的控件。滑块可以前后拖动。我们可以通过拖动选择一个特定的值。有时使用滑动条比直接输入数字或使用旋转框更加自然。

在下面的例子中,我们会显示一个滑动条与一个标签,标签用于显示图片,并通过滑动条控件图片的显示 。

注意这里图片只能用ico格式的,png格式图片会显示不了。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows a QSlider widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QSlider,
QLabel, QApplication)
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap class Example(QWidget): def __init__(self):
super().__init__() self.initUI() def initUI(self): sld = QSlider(Qt.Horizontal, self)
sld.setFocusPolicy(Qt.NoFocus)
sld.setGeometry(30, 40, 100, 30)
sld.valueChanged[int].connect(self.changeValue) self.label = QLabel(self)
self.label.setPixmap(QPixmap('audio.ico'))
self.label.setGeometry(160, 40, 80, 30) self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('QSlider')
self.show() def changeValue(self, value): if value == 0:
self.label.setPixmap(QPixmap('audio.ico'))
elif value > 0 and value <= 30:
self.label.setPixmap(QPixmap('min.ico'))
elif value > 30 and value < 80:
self.label.setPixmap(QPixmap('med.ico'))
else:
self.label.setPixmap(QPixmap('max.ico')) if __name__ == '__main__': app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

例子中我们模拟了一个音量控制。通过拖动滑块来改变标签上的图像。

sld = QSlider(Qt.Horizontal, self)

创建一个水平滑块

self.label = QLabel(self)
self.label.setPixmap(QPixmap('mute.png'))

创建了一个QLabel控件并为它设置了一个初始音量图像。

sld.valueChanged[int].connect(self.changeValue)

我们将valueChanged信号连接到自定义的changeValue()方法。

if value == 0:
self.label.setPixmap(QPixmap('mute.png'))
...

我们根据滑动条的值来设置标签的图像。在上面的代码中,当滑动条的值为0时我们为标签设置audio.ico图像。

进度条QProgressBar

一个进度条是一个显示任务进展的控件。QProgressBar控件提供了一个水平或垂直PyQt5工具包的进度条。程序员可以设置进度条的最小和最大值。默认值是0到99。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows a QProgressBar widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QProgressBar,
QPushButton, QApplication)
from PyQt5.QtCore import QBasicTimer class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self): self.pbar = QProgressBar(self)
self.pbar.setGeometry(30, 40, 200, 25) self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.doAction) self.timer = QBasicTimer()
self.step = 0 self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('QProgressBar')
self.show() def timerEvent(self, e): if self.step >= 100:
self.timer.stop()
self.btn.setText('Finished')
return self.step = self.step + 1
self.pbar.setValue(self.step) def doAction(self): if self.timer.isActive():
self.timer.stop()
self.btn.setText('Start')
else:
self.timer.start(100, self)
self.btn.setText('Stop') if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

这个例子显示一个水平的进度条和一个按钮,用户通过点击按钮开始和停止进度条

self.pbar = QProgressBar(self)

QProgressBar的构造方法

self.timer = QtCore.QBasicTimer()

我们使用定时器timer来激活QProgressBar

self.timer.start(100, self)

我们调用start()方法启动一个计时器。这个方法有两个参数:超时和对象将接收的事件。

def timerEvent(self, e):

    if self.step >= 100:

        self.timer.stop()
self.btn.setText('Finished')
return self.step = self.step + 1
self.pbar.setValue(self.step)

每个QObject及其子类都有一个timerEvent()事件处理器。我们要重新实现这个事件处理器来响应定时器事件。

def doAction(self):

    if self.timer.isActive():
self.timer.stop()
self.btn.setText('Start') else:
self.timer.start(100, self)
self.btn.setText('Stop')

我们在doAction()方法中启动/停止定时器。

日历控件 QCalendarWidget

QCalendarWidget提供了一个基于月份的日历控件。它使用户以一种简单直观的方式来选择日期。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows a QCalendarWidget widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QCalendarWidget,
QLabel, QApplication)
from PyQt5.QtCore import QDate class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
cal = QCalendarWidget(self)
cal.setGridVisible(True)
cal.move(20, 20)
cal.clicked[QDate].connect(self.showDate) self.lbl = QLabel(self)
date = cal.selectedDate()
self.lbl.setText(date.toString())
self.lbl.move(130, 260) self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Calendar')
self.show() def showDate(self, date):
self.lbl.setText(date.toString()) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中创建了一个日历控件和一个标签控件。选择的日期会显示在标签控件中。

cal = QCalendarWidget(self)

QCalendarWidget被创建

cal.clicked[QDate].connect(self.showDate)

如果我们从部件选择一个日期,点击[QDate]发出信号。我们将这个信号连接到用户定义的showDate()方法。

def showDate(self, date):     

    self.lbl.setText(date.toString())

我们检索所选日期通过调用selectedDate()方法。然后我们将date对象转换为字符串,并将其设置为小部件的标签。

七、PyQt5控件2

在这里我们将继续介绍PyQt5控件。我们将介绍QPixmap、QLineEdit QSplitter,QComboBox。

QPixmap

QPixmap是用于处理图像的控件。是优化的显示图像在屏幕上。在我们的代码示例中,我们将使用QPixmap窗口显示一个图像。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we dispay an image
on the window. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QHBoxLayout,
QLabel, QApplication)
from PyQt5.QtGui import QPixmap class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
hbox = QHBoxLayout(self)
pixmap = QPixmap("icon.png") lbl = QLabel(self)
lbl.setPixmap(pixmap) hbox.addWidget(lbl)
self.setLayout(hbox) self.move(300, 200)
self.setWindowTitle('Red Rock')
self.show() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在窗口上显示一个图片

pixmap = QPixmap("icon.png")

创建一个QPixmap 对象,它将传入的文件名作为参数。

lbl = QLabel(self)
lbl.setPixmap(pixmap)

我们将这个pixmap放到QLabel控件中。

文本框 QLineEdit

QLineEdit是用于输入或编辑单行文本的控件。它还有撤销重做、剪切复制和拖拽功能。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows text which
is entered in a QLineEdit
in a QLabel widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QLabel,
QLineEdit, QApplication) class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.lbl = QLabel(self)
qle = QLineEdit(self) qle.move(60, 100)
self.lbl.move(60, 40) qle.textChanged[str].connect(self.onChanged) self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('QLineEdit')
self.show() def onChanged(self, text):
self.lbl.setText(text)
self.lbl.adjustSize() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中展示了一个QLineEdit与一个QLabel。我们在QLineEdit中输入的文字会实时显示在QLabel控件中。

qle = QLineEdit(self)

创建QLineEdit

qle.textChanged[str].connect(self.onChanged)

文本框的内容发生改变的时候,会调用onChanged方法

def onChanged(self, text):

    self.lbl.setText(text)
self.lbl.adjustSize()

在onChanged()方法中我们将QLabel控件的文本设置为输入的内容。通过调用adjustSize()方法将QLabel控件的尺寸调整为文本的长度。

QSplitter

通过QSplitter,用户可以拖动子控件边界来调整子控件的尺寸。在下面的示例中,我们展示了三个由两个QSplitter组织的QFrame控件。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows
how to use QSplitter widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QHBoxLayout, QFrame,
QSplitter, QStyleFactory, QApplication)
from PyQt5.QtCore import Qt class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
hbox = QHBoxLayout(self) topleft = QFrame(self)
topleft.setFrameShape(QFrame.StyledPanel) topright = QFrame(self)
topright.setFrameShape(QFrame.StyledPanel) bottom = QFrame(self)
bottom.setFrameShape(QFrame.StyledPanel) splitter1 = QSplitter(Qt.Horizontal)
splitter1.addWidget(topleft)
splitter1.addWidget(topright) splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter1)
splitter2.addWidget(bottom) hbox.addWidget(splitter2)
self.setLayout(hbox) self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QSplitter')
self.show() def onChanged(self, text):
self.lbl.setText(text)
self.lbl.adjustSize() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中我们创建了三个QFrame与两个QSplitter。注意在某些主题中这些QSplitter可能会不可见。

topleft = QFrame(self)
topleft.setFrameShape(QFrame.StyledPanel)

我们使用一个风格框架为了看到QFrame小部件之间的界限。

splitter1 = QSplitter(Qt.Horizontal)
splitter1.addWidget(topleft)
splitter1.addWidget(topright)

我们创建一个QSplitter小部件和添加两个帧。

splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter1)

我们也可以将QSplitter添加到另一个QSplitter控件中。

下拉列表 QComboBox

QComboBox是允许用户从下拉列表中进行选择的控件。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example shows how to use
a QComboBox widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QLabel,
QComboBox, QApplication) class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.lbl = QLabel("Ubuntu", self) combo = QComboBox(self)
combo.addItem("Ubuntu")
combo.addItem("Mandriva")
combo.addItem("Fedora")
combo.addItem("Arch")
combo.addItem("Gentoo") combo.move(50, 50)
self.lbl.move(50, 150) combo.activated[str].connect(self.onActivated) self.setGeometry(300, 300, 300, 200)
self.setWindowTitle('QComboBox')
self.show() def onActivated(self, text):
self.lbl.setText(text)
self.lbl.adjustSize() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中展示了一个QComboBox与一个QLabel,QComboBox控件中有5个选项(Linux系统的几个发行版名称)。QLabel控件会显示QComboBox中选中的某个选项。

combo = QComboBox(self)
combo.addItem("Ubuntu")
combo.addItem("Mandriva")
combo.addItem("Fedora")
combo.addItem("Arch")
combo.addItem("Gentoo")

创建了一个有五个选项的QComboBox

combo.activated[str].connect(self.onActivated) 

当选中某个条目时会调用onActivated()方法。

def onActivated(self, text):

    self.lbl.setText(text)
self.lbl.adjustSize()

在方法中我们将QLabel控件的内容设置为选中的条目,然后调整它的尺寸。

八、PyQt5拖拽

在这部分PyQt5教程中,我们将讨论拖拽相关操作。

在计算机图形用户界面中,拖放的操作(或支持的作用)点击虚拟对象和拖动到另一个位置或到另一个虚拟对象。一般来说,它可以用于调用多种行动,或创建各种类型的两个抽象对象之间的关联。
拖放是图形用户界面的一部分。拖拽操作让用户直观地做复杂的事情。
通常,我们可以拖放两件事:数据或一些图形对象。如果我们把一个图像从一个应用程序到另一个地方,我们拖拽二进制数据。如果我们把一个标签在Firefox中并将其移动到另一个地方,我们拖拽一个图形组件。

简单拖放

在第一个示例中,我们有一个QLineEdit QPushButton。我们拖着纯文本的行编辑窗口小部件,然后放到按钮部件。按钮的标签会改变。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This is a simple drag and
drop example. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QPushButton, QWidget,
QLineEdit, QApplication) class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent) self.setAcceptDrops(True) def dragEnterEvent(self, e): if e.mimeData().hasFormat('text/plain'):
e.accept()
else:
e.ignore() def dropEvent(self, e): self.setText(e.mimeData().text()) class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
edit = QLineEdit('', self)
edit.setDragEnabled(True)
edit.move(30, 65) button = Button("Button", self)
button.move(190, 65) self.setWindowTitle('Simple drag & drop')
self.setGeometry(300, 300, 300, 150) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()

这个列子演示了一个简单的拖拽操作

class Button(QPushButton):

    def __init__(self, title, parent):
super().__init__(title, parent) self.setAcceptDrops(True)

我们需要重新实现某些方法才能使QPushButton接受拖放操作。因此我们创建了继承自QPushButton的Button类。

self.setAcceptDrops(True)

使该控件接受drop(放下)事件。

def dragEnterEvent(self, e):

    if e.mimeData().hasFormat('text/plain'):
e.accept()
else:
e.ignore()

首先我们重新实现了dragEnterEvent()方法,并设置可接受的数据类型(在这里是普通文本)。

def dropEvent(self, e):

    self.setText(e.mimeData().text()) 

通过重新实现dropEvent()方法,我们定义了在drop事件发生时的行为。这里我们改变了按钮的文字。

edit = QLineEdit('', self)
edit.setDragEnabled(True)

QLineEdit内置了对drag(拖动)操作的支持。我们只需要调用setDragEnabled()方法就可以了。

拖放一个按钮

在下面的示例中我们将演示如何对一个按钮控件进行拖放。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This is a simple drag and
drop example. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QPushButton, QWidget, QApplication
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag class Button(QPushButton):
def __init__(self, title, parent):
super().__init__(title, parent) def mouseMoveEvent(self, e): if e.buttons() != Qt.RightButton:
return mimeData = QMimeData() drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft()) dropAction = drag.exec_(Qt.MoveAction) def mousePressEvent(self, e): QPushButton.mousePressEvent(self, e) if e.button() == Qt.LeftButton:
print('press') class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.setAcceptDrops(True) self.button = Button('Button', self)
self.button.move(100, 65) self.setWindowTitle('Click or Move')
self.setGeometry(300, 300, 280, 150) def dragEnterEvent(self, e):
e.accept() def dropEvent(self, e):
position = e.pos()
self.button.move(position) e.setDropAction(Qt.MoveAction)
e.accept() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
ex.show()
app.exec_()

在这个例子中,在窗口显示一个QPushButton 。如果用鼠标左键点击这个按钮会在控制台中输出’press’消息。鼠标右击进行拖动。

class Button(QPushButton):

    def __init__(self, title, parent):
super().__init__(title, parent)

我们从QPushButton派生了一个Button类,并重新实现了mouseMoveEvent()与mousePressEvent()方法。mouseMoveEvent()方法是拖放操作产生的地方。

if e.buttons() != Qt.RightButton:
return

在这里我们设置只在鼠标右击时才执行拖放操作。鼠标左击用于按钮的点击事件。

mimeData = QMimeData()

drag = QDrag(self)
drag.setMimeData(mimeData)
drag.setHotSpot(e.pos() - self.rect().topLeft())

QDrag提供了对基于MIME的拖放的数据传输的支持。

dropAction = drag.exec_(Qt.MoveAction)

Drag对象的exec_()方法用于启动拖放操作。

def mousePressEvent(self, e):

    QPushButton.mousePressEvent(self, e)

    if e.button() == Qt.LeftButton:
print('press')

鼠标左击按钮时我们会在控制台打印‘press’。注意我们也调用了父按钮的mousePressEvent()方法。否则会看不到按钮的按下效果。

position = e.pos()
self.button.move(position)

释放右键后调用dropEvent()方法中,即找出鼠标指针的当前位置,并将按钮移动过去。

e.setDropAction(Qt.MoveAction)
e.accept()

我们可以对指定的类型放弃行动。在我们的例子中它是一个移动动作。

九、PyQt5绘图

PyQt5绘画系统能够呈现矢量图形,图像,和大纲font-based文本。我们也可以在程序中调用系统api自定义绘图控件。

绘图要在paintEvent()方法中实现。在QPainter对象的begin()与end()方法间编写绘图代码。它会在控件或其他图形设备上进行低级的图形绘制。

绘制文本

我们先以窗体内Unicode文本的绘制为例。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we draw text in Russian azbuka. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QFont
from PyQt5.QtCore import Qt class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439: \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430' self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Draw text')
self.show() def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.drawText(event, qp)
qp.end() def drawText(self, event, qp):
qp.setPen(QColor(168, 34, 3))
qp.setFont(QFont('Decorative', 10))
qp.drawText(event.rect(), Qt.AlignCenter, self.text) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在我们的示例中,我们绘制一些Cylliric文本。文本垂直和水平对齐。

def paintEvent(self, event):
...

绘制工作在paintEvent的方法内部完成。

qp = QPainter()
qp.begin(self)
self.drawText(event, qp)
qp.end()

QPainter类负责所有的初级绘制。之间的所有绘画方法去start()和end()方法。实际的绘画被委托给drawText()方法。

qp.setPen(QColor(168, 34, 3))
qp.setFont(QFont('Decorative', 10))

在这里,我们定义一个画笔和一个字体用于绘制文本。

qp.drawText(event.rect(), Qt.AlignCenter, self.text)

drawText()方法将文本绘制在窗体,显示在中心

画点

点是可以绘制的最简单的图形对象。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In the example, we draw randomly 1000 red points
on the window. author: py40.com
last edited: 2017年3月
"""
import sys, random
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.setGeometry(300, 300, 280, 170)
self.setWindowTitle('Points')
self.show() def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawPoints(qp)
qp.end() def drawPoints(self, qp):
qp.setPen(Qt.red)
size = self.size() for i in range(1000):
x = random.randint(1, size.width() - 1)
y = random.randint(1, size.height() - 1)
qp.drawPoint(x, y) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在这例子中,我们在窗口上随机绘制了1000个红点

qp.setPen(Qt.red)

设置画笔为红色,我们使用了预定义的Qt.red常量

size = self.size()

每次我们改变窗口的大小,生成一个 paint event 事件。我们得到的当前窗口的大小size。我们使用窗口的大小来分配点在窗口的客户区。

qp.drawPoint(x, y)

通过drawpoint绘制圆点

颜色

颜色是一个对象代表红、绿、蓝(RGB)强度值。有效的RGB值的范围从0到255。我们可以用不同的方法定义了一个颜色。最常见的是RGB十进制或十六进制值的值。我们也可以使用一个RGBA值代表红色,绿色,蓝色,透明度。我们添加一些额外的信息透明度。透明度值255定义了完全不透明,0是完全透明的,例如颜色是无形的。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example draws three rectangles in three
#different colours. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QBrush class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.setGeometry(300, 300, 350, 100)
self.setWindowTitle('Colours')
self.show() def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawRectangles(qp)
qp.end() def drawRectangles(self, qp):
col = QColor(0, 0, 0)
col.setNamedColor('#d4d4d4')
qp.setPen(col) qp.setBrush(QColor(200, 0, 0))
qp.drawRect(10, 15, 90, 60) qp.setBrush(QColor(255, 80, 0, 160))
qp.drawRect(130, 15, 90, 60) qp.setBrush(QColor(25, 0, 90, 200))
qp.drawRect(250, 15, 90, 60) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

实例中我们绘制了3个不同颜色的矩形

color = QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')

在这里,我们定义一个使用十六进制符号颜色。

qp.setBrush(QColor(200, 0, 0))
qp.drawRect(10, 15, 90, 60)

我们为QPainter设置了一个笔刷(Bursh)对象并用它绘制了一个矩形。笔刷是用于绘制形状背景的基本图形对象。drawRect()方法接受四个参数,前两个是起点的x,y坐标,后两个是矩形的宽和高。这个方法使用当前的画笔与笔刷对象进行绘制。

QPen(画笔)

QPen是一个基本的图形对象。用于绘制线条、曲线和轮廓的矩形、椭圆、多边形或其他形状。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example we draw 6 lines using
different pen styles. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QColor, QPen
from PyQt5.QtCore import Qt class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.setGeometry(300, 300, 280, 270)
self.setWindowTitle('Pen styles')
self.show() def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawLines(qp)
qp.end() def drawLines(self, qp):
pen = QPen(Qt.black, 2, Qt.SolidLine) qp.setPen(pen)
qp.drawLine(20, 40, 250, 40) pen.setStyle(Qt.DashLine)
qp.setPen(pen)
qp.drawLine(20, 80, 250, 80) pen.setStyle(Qt.DashDotLine)
qp.setPen(pen)
qp.drawLine(20, 120, 250, 120) pen.setStyle(Qt.DotLine)
qp.setPen(pen)
qp.drawLine(20, 160, 250, 160) pen.setStyle(Qt.DashDotDotLine)
qp.setPen(pen)
qp.drawLine(20, 200, 250, 200) pen.setStyle(Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
qp.setPen(pen)
qp.drawLine(20, 240, 250, 240) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中我们画六行。线条勾勒出了六个不同的笔风格。有五个预定义的钢笔样式。我们也可以创建自定义的钢笔样式。最后一行使用一个定制的钢笔绘制风格。

pen = QPen(Qt.black, 2, Qt.SolidLine)

我们创建一个QPen对象。颜色是黑色的。宽度设置为2像素,这样我们可以看到笔风格之间的差异。Qt.SolidLine是预定义的钢笔样式。

pen.setStyle(Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
qp.setPen(pen)

这里我们定义了一个画笔风格。我们设置了Qt.CustomDashLine并调用了setDashPattern()方法,它的参数(一个数字列表)定义了一种风格,必须有偶数个数字;其中奇数表示绘制实线,偶数表示留空。数值越大,直线或空白就越大。这里我们定义了1像素的实线,4像素的空白,5像素实线,4像素空白。。。

QBrush(笔刷)

QBrush是一个基本的图形对象。它用于油漆的背景图形形状,如矩形、椭圆形或多边形。三种不同类型的刷可以:一个预定义的刷,一个梯度,或纹理模式。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial This example draws 9 rectangles in different
brush styles. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtGui import QPainter, QBrush
from PyQt5.QtCore import Qt class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
self.setGeometry(300, 300, 355, 280)
self.setWindowTitle('Brushes')
self.show() def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
self.drawBrushes(qp)
qp.end() def drawBrushes(self, qp):
brush = QBrush(Qt.SolidPattern)
qp.setBrush(brush)
qp.drawRect(10, 15, 90, 60) brush.setStyle(Qt.Dense1Pattern)
qp.setBrush(brush)
qp.drawRect(130, 15, 90, 60) brush.setStyle(Qt.Dense2Pattern)
qp.setBrush(brush)
qp.drawRect(250, 15, 90, 60) brush.setStyle(Qt.DiagCrossPattern)
qp.setBrush(brush)
qp.drawRect(10, 105, 90, 60) brush.setStyle(Qt.Dense5Pattern)
qp.setBrush(brush)
qp.drawRect(130, 105, 90, 60) brush.setStyle(Qt.Dense6Pattern)
qp.setBrush(brush)
qp.drawRect(250, 105, 90, 60) brush.setStyle(Qt.HorPattern)
qp.setBrush(brush)
qp.drawRect(10, 195, 90, 60) brush.setStyle(Qt.VerPattern)
qp.setBrush(brush)
qp.drawRect(130, 195, 90, 60) brush.setStyle(Qt.BDiagPattern)
qp.setBrush(brush)
qp.drawRect(250, 195, 90, 60) if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

示例中绘制九个不同的矩形

brush = QBrush(Qt.SolidPattern)
qp.setBrush(brush)
qp.drawRect(10, 15, 90, 60)

我们定义了一个笔刷对象,然后将它设置给QPainter对象,并调用painter的drawRect()方法绘制矩形。

十、PyQt5自定义控件

PyQt5包含种类丰富的控件。但能满足所有需求的控件库是不存在的。通常控件库只提供了像按钮、文本控件、滑块等最常用的控件。但如果需要某种特殊的控件,我们只能自己动手来实现。 自定义控件需要使用工具库提供的绘图工具,可能有两种方式:在已有的控件上进行拓展或从头开始创建自定义控件。

Burning widget(烧录控件)

这个控件可能会在Nero,K3B或其他CD/DVD烧录软件中见到。

# -*- coding: utf-8 -*-

"""
PyQt5 tutorial In this example, we create a custom widget. author: py40.com
last edited: 2017年3月
"""
import sys
from PyQt5.QtWidgets import (QWidget, QSlider, QApplication,
QHBoxLayout, QVBoxLayout)
from PyQt5.QtCore import QObject, Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QFont, QColor, QPen class Communicate(QObject):
updateBW = pyqtSignal(int) class BurningWidget(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self): self.setMinimumSize(1, 30)
self.value = 75
self.num = [75, 150, 225, 300, 375, 450, 525, 600, 675] def setValue(self, value): self.value = value def paintEvent(self, e): qp = QPainter()
qp.begin(self)
self.drawWidget(qp)
qp.end() def drawWidget(self, qp): font = QFont('Serif', 7, QFont.Light)
qp.setFont(font) size = self.size()
w = size.width()
h = size.height() step = int(round(w / 10.0)) till = int(((w / 750.0) * self.value))
full = int(((w / 750.0) * 700)) if self.value >= 700: qp.setPen(QColor(255, 255, 255))
qp.setBrush(QColor(255, 255, 184))
qp.drawRect(0, 0, full, h)
qp.setPen(QColor(255, 175, 175))
qp.setBrush(QColor(255, 175, 175))
qp.drawRect(full, 0, till - full, h) else: qp.setPen(QColor(255, 255, 255))
qp.setBrush(QColor(255, 255, 184))
qp.drawRect(0, 0, till, h) pen = QPen(QColor(20, 20, 20), 1,
Qt.SolidLine) qp.setPen(pen)
qp.setBrush(Qt.NoBrush)
qp.drawRect(0, 0, w - 1, h - 1) j = 0 for i in range(step, 10 * step, step):
qp.drawLine(i, 0, i, 5)
metrics = qp.fontMetrics()
fw = metrics.width(str(self.num[j]))
qp.drawText(i - fw / 2, h / 2, str(self.num[j]))
j = j + 1 class Example(QWidget):
def __init__(self):
super().__init__() self.initUI() def initUI(self):
sld = QSlider(Qt.Horizontal, self)
sld.setFocusPolicy(Qt.NoFocus)
sld.setRange(1, 750)
sld.setValue(75)
sld.setGeometry(30, 40, 150, 30) self.c = Communicate()
self.wid = BurningWidget()
self.c.updateBW[int].connect(self.wid.setValue) sld.valueChanged[int].connect(self.changeValue)
hbox = QHBoxLayout()
hbox.addWidget(self.wid)
vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setLayout(vbox) self.setGeometry(300, 300, 390, 210)
self.setWindowTitle('Burning widget')
self.show() def changeValue(self, value):
self.c.updateBW.emit(value)
self.wid.repaint() if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

在示例中我们使用了滑块与一个自定义控件。自定义控件受滑块控制。控件显示了媒体介质的容量和剩余空间。该控件的最小值为1,最大值为750。在值超过700时颜色变为红色。这通常意味着超刻(即实际写入光盘的容量超过刻录盘片官方标称容量的一种操作)。

BurningWidget控件通过QHBoxLayout与QVBoxLayout置于窗体的底部。

class BurningWidget(QWidget):

    def __init__(self):
super().__init__()

烧录的控件,它基于QWidget

self.setMinimumSize(1, 30)

我们改变了控件的最小大小(高度),默认值为有点小。

font = QFont('Serif', 7, QFont.Light)
qp.setFont(font)

我们使用一个比默认要小的字体。

size = self.size()
w = size.width()
h = size.height() step = int(round(w / 10.0)) till = int(((w / 750.0) * self.value))
full = int(((w / 750.0) * 700))

控件采用了动态绘制技术。窗体越大,控件也随之变大;反之亦然。这也是我们需要计算自定义控件的载体控件(即窗体)尺寸的原因。till参数定义了需要绘制的总尺寸,它根据slider控件计算得出,是整体区域的比例值。full参数定义了红色区域的绘制起点。注意在绘制时为取得较大精度而使用的浮点数运算。

实际的绘制分三个步骤。黄色或红黄矩形的绘制,然后是刻度线的绘制,最后是刻度值的绘制。

metrics = qp.fontMetrics()
fw = metrics.width(str(self.num[j]))
qp.drawText(i-fw/2, h/2, str(self.num[j]))

我们使用字体度量来绘制文本。我们必须知道文本的宽度,以中心垂直线。

def changeValue(self, value):

    self.c.updateBW.emit(value)
self.wid.repaint()

当滑块发生移动时,changeValue()方法会被调用。在方法内我们触发了一个自定义的updateBW信号,其参数是当前滚动条的值。该值被用于计算Burning widget的容量值。然后对控件进行重绘。

最新文章

  1. WWW读取安卓外部音乐文件
  2. 【GOF23设计模式】单例模式
  3. 一名小小的SQL Server DBA想谈一下SQL Server的能力
  4. bootstrap的下拉框在firefox界面不友好的处理
  5. map遍历方法
  6. Spring-2-J Goblin Wars(SPOJ AMR11J)解题报告及测试数据
  7. css + html 小知识总结
  8. OpenJudge就算概论-过滤多余的空格
  9. Mac OS 上设置 JAVA_HOME
  10. c# 轻量级ORM框架 之 WhereHelper (二)
  11. 学习python——博客记录第一天
  12. 关于高性能javascript 笔记
  13. HTML5 预加载
  14. Django异步任务之Celery
  15. ueditor上传图片尺寸过大导致显示难看的解决办法
  16. Android 解决通过自定义设置打开热点后手机搜索不到热点的问题。
  17. bash 基础命令
  18. Web Deploy发布网站错误 检查授权和委派设置
  19. 【LeetCode每天一题】Rotate Image(旋转矩阵)
  20. IIS7.0 下使用Intelligencia.UrlRewriter时Session为空问题

热门文章

  1. vh 存在问题?试试动态视口单位之 dvh、svh、lvh
  2. 实现简单的`Blazor`低代码
  3. el-input 限制只能输入正整数
  4. Cannot find module ‘xxx\node_modules\yorkie\bin\install.js‘
  5. 有趣的python库-pillow
  6. 【模板】AC自动机(二次加强版)
  7. MySQL视图、存储过程、函数、触发器、定时任务、流程控制总结
  8. 内网安全之:MS14-068 Kerberos 域用户提权漏洞
  9. 【django-vue】封装logger 封装全局异常 封装response 数据库配置 用户表继承AbstractUser配置
  10. dotnet core+nginx+supervisor