PyQt5简介
这是一个PyQt5的入门教程.目的是帮助你使用PyQt5.本教程创建并在Linux上测试.PyQt4教程则覆盖了PyQt4,对应Python的2.x和3.x的Qt4的库.
原作地址:http://zetcode.com/gui/pyqt5/
原翻译地址 :http://blog.csdn.net/neverstop_2009/article/category/5715885
官方WiKi: https://pypi.python.org/pypi/PyQt5/
关于PyQt5
PyQt5是来自Digia的Qt5应用框架的Python工具集.它可用于Python 2.x和3.x.本教程使用了Pythong3.Qt库是最强大的GUI库之一.PyQt5的官方网址是www.riverbankcomputing.co.uk/news.PyQt5由Riverbank Computing开发.
PyQt5是作为一套Python模块实现的.它有超过620个的类和6000个函数和方法.它是一个跨平台的工具集,可以运行在所有的主流操作系统上,包括Unix, Windows和Mac OS.PyQt5有两种许可,开发者可以在GPL和商业许可证之间选择.
PyQt5的类划分为以下几个模块,包括:
QtCore
QtGui
QtWidgets
QtMultimedia
QtBluetooth
QtNetwork
QtPositioning
Enginio
QtWebSockets
QtWebKit
QtWebKitWidgets
QtXml
QtSvg
QtSql
QtTest
QtCore模块包含了核心非GUI功能.这个模块是用于时间、文件和目录、变量数量类型、数据流、URLs、mime(多用途因特网邮件扩展)类型、线程和进程。
QtGui模块包含了窗口系统集成、事件处理、2D绘画、基本成像、字体和文本.
QtWidgets模块包含了一系列UI元素,用于创建典型的桌面风格用户接口.
QtMultimedia包含了处理多媒体内容和访问摄像机、无线电等功能的APIs.
QtBluetooth模块包含了设备扫描和设备接接与互动.
QtNetwork模块包含了网络编程的类.这些类用于TCP/IP和UDP客户端、服务端编程,使之网络编程更加方便和快捷.
QtPositioning模块包含了使用一系列包括卫星、Wi-Fi或文本文件在内的可能源变量来决定位置的类.
Enginio模块包含了用于访问Qt云服务管理应用运行时的客户端的库.
QtWebSockets模块包含了实现WebSocket协议的类.
QtWebKit包含了基于WebKit2库的实现网络浏览器的类.
QtWebKitWidgets包含了基于WebKit1库的实现网络浏览器的类,它是用在基于应用的QtWidgets上的.
QtXml包含了用于XML文件的类.这个模块提供了SAX和DOM接口的实现.
QtSvg模块提供了用于显示SVG文件内容的类.SVG(Scalable Vector Graphics)是用于描述在XML中二维图像和图像应用的语言.
QtSql模块提供了用于数据库的类.
QtSql包含了PyQt5的单元测试功能.
PyQt4和PyQt5的不同
PyQt是不兼容PyQt4的.在PyQt5中主要有以下几个更改.然而,把旧代码修改为新库不是什么难事.困难在于:
Python已经重组.一些模块被抛弃(QtScript),其他的一些则被分隔为几个子模块(QtGui, QtWebKit).
新模块被引进,包括QtBluetooth, QtPositioning和Enginio.
PyQt5只支持新形式的信号和槽处理.SIGNAL()和SLOT()不再支持.
PyQt5不再支持任何在Qt5.0被标记弃用和淘汰的中Qt API.
PyQt5程序安装
Installation
PyQt5 source packages for the GPL version can be dowloaded from https://www.riverbankcomputing.com/software/pyqt/download5/.
Wheels for the GPL version for 32 and 64-bit Windows, 64-bit OS X and 64-bit Linux can be installed from PyPI:
pip3 install PyQt5
The wheels include a copy of the required parts of the LGPL version of Qt.
如果要安装pyqt-designer可以安装扩展包等扩展工具可以调用命令如下:
pip install PyQt5-tools
之后Desinger出现在:C:\Users\dragon\Python\Python35-32\Lib\site-packages\pyqt5-tools 目录
第一个PyQt5程序
在这部分,我们学习一些基本功能.
简单的例子
这是一个简单的例子,它显示一个小窗口.我们可以对这个窗口做很多事情,我们可以调整它的大小,最大化它或最小化它.这需要很多代码.但是已经有人编写了这个功能.因为它在绝大多数的应用中重复出现,因此没有必要重复的编写它.PyQt5是高级工具集.如果我们把它当作一个低级工具集,那么下面的代码例子可能要有几百行.
- import sys
- from PyQt5.QtWidgets import QApplication, QWidget
-
-
- if __name__ == '__main__':
-
- app = QApplication(sys.argv)
-
- w = QWidget()
- w.resize(250, 150)
- w.move(300, 300)
- w.setWindowTitle('Simple')
- w.show()
-
- sys.exit(app.exec_())
以上代码会在屏幕上显示一个小窗口.
- import sys
- from PyQt5.QtWidgets import QApplication, QWidget
这们在这里进行必要的导入.基本的窗口集在PyQt5.QtWidgets模块里.
- app = QApplication(sys.argv)
每一个PyQt5应用都必须创建一个应用对象.sys.argv参数是来自命令行的参数列表.Python脚本可以从shell里运行.这是我们如何控制我们的脚本运行的一种方法.
QWidget窗口是PyQt5中所有用户界口对象的基本类.我们使用了QWidget默认的构造器.默认的构造器没有父类.一个没有父类的窗口被称为一个window.
resize()方法调整了窗口的大小.被调整为250像素宽和150像素高.
move()方法移动了窗口到屏幕坐标x=300, y=300的位置.
- w.setWindowTitle('Simple')
在这里我们设置了窗口的标题.标题会被显示在标题栏上.
show()方法将窗口显示在屏幕上.一个窗口是先在内存中被创建,然后显示在屏幕上的.
最后,我们进入应用的主循环.事件处理从这里开始.主循环从窗口系统接收事件,分派它们到应用窗口.如果我们调用了exit()方法或者主窗口被销毁,则主循环结束.sys.exit()方法确保一个完整的退出.环境变量会被通知应用是如何结束的.
exec_()方法是有一个下划线的.这是因为exec在Python中是关键字.因此,用exec_()代替.
图片:简单的例子
一个应用图标
一个应用图标是一个用于显示在标题栏最左上角的小图像.在下面的例子中我们会在PyQt5中演示,同时我们还会介绍一个新的方法.
- import sys
- from PyQt5.QtWidgets import QApplication, QWidget
- from PyQt5.QtGui import QIcon
-
-
- class Example(QWidget):
-
- def __init__(self):
- super().__init__()
-
- self.initUI()
-
-
- def initUI(self):
-
- self.setGeometry(300, 300, 300, 220)
- self.setWindowTitle('Icon')
- 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__()
- ...
在面向对象编程中重要的是类、数据和方法.在这里,我们创建了一个叫Example的新类.Example类是从QWidget类继承下来的.这意味着我们可以调用两个构造器:对于Example类的第一个和继承类的第二个.super()方法返回的是Example类的父对象,我们调用了它的构造器.__init__()方法是Python语言中的一个构造方法.
GUI的创建是委托给initUI()方法的.
- self.setGeometry(300, 300, 300, 220)
- self.setWindowTitle('Icon')
- self.setWindowIcon(QIcon('web.png'))
这三个方法都是继承于QWidget类.setGeometry()方法做两件事:定位窗口在屏幕上的位置和设置它的大小.前面两个参数是窗口的x和y轴位置.第三个参数是窗口的宽,第四个参数是窗口的高.实际上,它是把resize()和move()这两个方法合并成了一个方法.后面的方法是设置应用程序的图标.首先我们要创建一个QIcon对象,QIcon接收我们图标的路径并显示它(译者注:这里的路径是相对路径--相对于你的源代码所在位置;或者绝对路径).
- if __name__ == '__main__':
-
- app = QApplication(sys.argv)
- ex = Example()
- sys.exit(app.exec_())
应用程序和例子对象被创建.主循环开始.
图片:图标
显示一个提示信息
我们可以为我们的任何一个窗体提供悬浮帮助信息.
- 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):
-
- QToolTip.setFont(QFont('SansSerif', 10))
-
- self.setToolTip('This is a QWidget widget')
-
- btn = QPushButton('Button', self)
- btn.setToolTip('This is a QPushButton widget')
- 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_())
在这个例子里,我们为两个PyQt5窗体显示了提示信息.
- QToolTip.setFont(QFont('SansSerif', 10))
这个静态方法设置了一种字体用于绘制提示信息.我们使用了10像素的SansSerif字体.
- self.setToolTip('This is a QWidget widget')
要创建一个提示信息,我们要调用setTooltip()方法.我们可以直接用多格式文件来格式化它.
- btn = QPushButton('Button', self)
- btn.setToolTip('This is a QPushButton widget')
我们创建一个点击按钮并给它设置一个提示信息.
- btn.resize(btn.sizeHint())
- btn.move(50, 50)
这是用于把按钮定义大小和在窗体位置的.sizeHint()方法会给按钮一个推荐的大小.
图片:提示信息
关闭一个窗口
最明显的关闭一个窗口是点击标题栏上的x.在下个例子中,我们会显示如何以编程的方式去关闭我们的窗口.我们会接触一点信号和槽.
下面是一个QPushButton部件构造器,我们会用在例子中.
- QPushButton(string text, QWidget parent = None)
text参数是显示在按钮上的文本.parent是我们将按钮放置在哪里的部件.在我们这个例子,parent就是一个QWidget.一个应用程序的部件们会组成一个层次.在这个层次中,大多数的部件有着他们的父类.没有父类的部件就是最顶层的窗口.
- 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_())
在这个例子中,我们创建一个退出按钮.单击这个按钮时,应用程序就会终止.
- from PyQt5.QtCore import QCoreApplication
我们需要来自QtCore模块的对象.
- qbtn = QPushButton('Quit', self)
我们创建一个按钮.按钮是QPushButton类的一个实例.构造器的第一个参数是按钮的标签,第二个参数是父部件类.父部件是Example部件,是从QWidget继承来的.
- qbtn.clicked.connect(QCoreApplication.instance().quit)
在PyQt5中的事件处理系统是用信号和槽机制构建起来的.如果我们点击按钮,点击信号就会发出.槽可以是一个Qt槽或任何Python可调用的.QCoreApplication包含了主要的事件循环;它处理并分派所有事件.instance()方法给我们它当前的实例.注意QCoreApplication是用QApplication创建的.点击信息被连接到quit()方法,它会终止程序.通信是在发送者和接收者这两个对象之间完成的.这里的发送者是点击按钮,接收者是程序对象.
图片:退出按钮
消息框
在默认情况下,如果我们点击标题栏的x,QWidget会被关闭.有时我们想修改一个默认情况.例如,如果我们在编辑器里有一个被打开的文件,我们做了一些修改.我们会想显示一个消息框来确认操作.
- 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_())
如果我们关闭一个QWidget,QCloseEvent会被生成.要修改部件行为,我们需要重新实现closeEvent()事件处理器.
- reply = QMessageBox.question(self, 'Message',
- "Are you sure to quit?", QMessageBox.Yes |
- QMessageBox.No, QMessageBox.No)
我们显示一个有两个按钮的消息框:Yes和No.第一个串会显示在标题栏上.第二个字符串是对话框的消息文本.第三个参数指定了对话框中显示的按钮.最后一个参数是默认按钮.它是最初的键盘焦点.返回值为存储在reply变量里.
- if reply == QtGui.QMessageBox.Yes:
- event.accept()
- else:
- event.ignore()
在这里,我们测试返回值.如果我们点击了Yes按钮,我们会接受事件,它会导致部件关闭并终止程序.否则会忽略关闭事件.
图片:消息框
把窗口放在屏幕正中间
下面的脚本会显示我们如何并一个窗口放到桌面正中间.
- 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类担任了用户的桌面信息,包括屏幕大小.
这条代码会通过调用自定义的center()方法把窗口放到正中间.
- qr = self.frameGeometry()
我们通过这个得到主窗体的矩形说明.这包含任何窗口框架.
- cp = QDesktopWidget().availableGeometry().center()
我们算出显示器的屏幕分辨率.通过这个,我们会得到中心点.
我们的矩形已经有了它的宽和高.现在我们设置矩形的中心点到屏幕的中心.矩形的尺寸没有被修改.
我们移动程序窗口的左上角到qr矩形的左上角,因此将窗口放到屏幕正中心.
图片:正中心
PyQt5中的菜单和工具栏
在这部分,我们会创建菜单和工具栏.一个菜单是在一个菜单条中的一组命令.一个工具栏是在程序中有一些常用命令的按钮.
主窗口
QMainWindow类提供了一个主程序窗口.这允许我们创建一个有状态栏、工具栏和菜单栏的典型程序架构.
状态栏
一个状态栏是一个用于显示状态信息的部件.
- 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')
要得到状态栏,我们要调用QtGui.QMainWindow类中的statusBar()方法.第一次调用这个方法会创建一个状态栏.随后的调用会返回状态栏对象.showMessage()会显示一条信息在状态栏上.
菜单栏
一个菜单栏是一个GUI程序的通用部分.它是在不同菜单中的一组命令(Mac OS对待菜单栏不同.要得到相同的输出,我们需要添加下面这条代码:menubar.setNativeMenuBar(False).)
- 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是一个菜单栏、工具栏或自定义键盘快捷键的动作被执行的抽象.在以上三行代码中,我们创建了一个动作,它指定了一个图标和一个Exit标签.而且还定义了一个快捷键.第三行代码创建了一个状态提示,当我们将鼠标放到这个菜单上的时候它会显示在状态栏上.
- exitAction.triggered.connect(qApp.quit)
当我们选择了这个指定的动作时,会触发一个信号.信号是被连接到QApplication部件的quit()方法.它会终止程序.
- menubar = self.menuBar()
- fileMenu = menubar.addMenu('&File')
- fileMenu.addAction(exitAction)
menuBar()方法创建一个菜单栏.我们创建一个file菜单,并将exit动作追加到它上面.
工具栏
菜单聚合了我们在程序中所有可以使用的命令.工具栏提供了最常用的命令的快捷访问.
- 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_())
在上面的例子中,我们创建了一个简单的工具栏.工具栏有一个工具动作,一个exit动作在被触发时会终止程序.
- exitAction = QAction(QIcon('exit24.png'), 'Exit', self)
- exitAction.setShortcut('Ctrl+Q')
- exitAction.triggered.connect(qApp.quit)
和上面的菜单栏例子相同,我们创建了一个动作对象.这个对象有一个标签、一个图标和一个快捷键.QtGui.QMainWindow的quit()方法被连接到触发的信号上.
- self.toolbar = self.addToolBar('Exit')
- self.toolbar.addAction(exitAction)
这就是我们创建了一个工具栏并添加一个动作对象的例子.
图片:工具栏
将它们放到一起
在这部分的最后一个例子,我们会创建一个菜单栏、工具栏和状态栏.我们还会创建一个中心部件.
- 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_())
这个代码例子创建了一个有菜单栏、工具栏和状态栏的典型GUI程序架构.
- textEdit = QTextEdit()
- self.setCentralWidget(textEdit)
在这里我们创建了一个文件编辑器部件.我们设置它为QMainWindow的中心部件.中心部件会占据所有的剩余区域.
图片:主窗口
在这部分,我们会研究程序中的事件和信号.
事件
所有的GUI程序都是事件驱动的.事件大多是程序的用户生成的.但是也可以被其他手段生成,例如一个Internet连接,一个窗口管理器,或者时间.当我们调用程序的exec_()方法时,程序会进入主循环.主循环会读取事件并将它们送到特定对象那里.
在事件模型中,有三个参与者:
事件源.
事件对象
事件目标.
事件源是那些状态变化的对象,它们生成事件.事件对象(事件)在事件源中封装状态变化.事件目标是那些想被通知的对象.事件源对象分派处理一个事件的任务到事件目标.
PyQt5有着一套独特的信号和槽机制来处理事件.信号和槽用于在对象之间通信.一个信号是在某特定事件发生时被发送的.一个槽可以是任何的Python调用.一个槽当它连接的信号被发送的时候被调用.
信号和槽
这是在PyQt5中演示信号和槽的简单例子.
- 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槽.
发送者是一个对象,它发送一个信号.接收者是一个对象,它接收信号.槽是作用于信号的方法.
图片:信号和槽
重载事件处理器
在PyQt5中的事件经常是被重载的事件处理器处理的.
- 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()
如果我们点击退出按钮,程序会被终止.
事件发送者
有些时候知道哪个部件是信号的发送者会很方便.因此,PyQt5有sender()方法.
- 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()方法来决定信号来源.在程序的状态栏,会显示按钮标签被点击.
图片:事件发送者
发送信号
从QObject创建的对象可以发送信号.在下面的例子我们会看到我们如何发送自定义信号.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- In this example, we show how to emit a
- signal.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- import sys
- from PyQt5.QtCore import pyqtSignal, QObject
- from PyQt5.QtWidgets import QMainWindow, QApplication
-
-
- class Communicate(QObject):
-
- closeApp = pyqtSignal()
-
-
- class Example(QMainWindow):
-
- def __init__(self):
- super().__init__()
-
- self.initUI()
-
-
- def initUI(self):
-
- self.c = Communicate()
- self.c.closeApp.connect(self.close)
-
- self.setGeometry(300, 300, 290, 150)
- self.setWindowTitle('Emit signal')
- self.show()
-
-
- def mousePressEvent(self, event):
-
- self.c.closeApp.emit()
-
-
- if __name__ == '__main__':
-
- app = QApplication(sys.argv)
- ex = Example()
- sys.exit(app.exec_())
我们创建一个新的信号叫closeApp.这个信号在鼠标被点击的时候被发送.信号连接到QMainWindow的close()槽.
- class Communicate(QObject):
-
- closeApp = pyqtSignal()
一个信号被创建,pyqtSignal()作为Communicate类的外部属性.
- self.c = Communicate()
- self.c.closeApp.connect(self.close)
自定义的closeApp信号被连接到QMainWindow的close()槽.
- def mousePressEvent(self, event):
-
- self.c.closeApp.emit()
当我们在窗口上点击鼠标的时候,closeApp信号就会被发送,程序就会被终止.
部件
部件是构建一个程序的基本组成部分.PyQt5有非常多的部件,包括按钮、复选框、滑动器和列表框.在这一章,我们会说到几个很有用的部件:QCheckBox、ToggleButton、QSlider、QProgressBar和QCalendarWidget.
QCheckBox
一个QCheckBox是一个有两种状态的部件:开或关.它是一个带标签的盒子.复选框是典型用途是在一个程序中展现特性,以便可以使其激活或禁止.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- In this example, a QCheckBox widget
- is used to toggle the title of a window.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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.stateChanged.connect(self.changeTitle)
我们选择用户自定义的changeTitle()到statChanged信号.changeTitle()方法会触发窗口标题.
- def changeTitle(self, state):
-
- if state == Qt.Checked:
- self.setWindowTitle('QCheckBox')
- else:
- self.setWindowTitle('')
部件的状态给到changeTitle()方法中的state变量.如果部件被选中了,就会给窗口设置一个标题.否则,就会给标题题设置一个空字符串.
图片:复选框
Toggle button
一个开关按钮是一个特定模式下的QPushButton.它是有两种状态的按钮:按下和没有按下.我们通过点击在这两种状态之间切换.有些时候这个功能很实用.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- In this example, we create three toggle buttons.
- They will control the background colour of a
- QFrame.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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)
-
- redb = QPushButton('Green', self)
- redb.setCheckable(True)
- redb.move(10, 60)
-
- redb.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_())
在这个例子中,我们创建了三个开关按钮和一个QWidget.我们设置QWidget的背景色为黑色.开关按钮会触发开关将颜色值变为红、绿和蓝.背景色会根据我们按的按钮而改变.
- self.col = QColor(0, 0, 0)
这是最开始的时候,颜色为黑色.
- redb = QPushButton('Red', self)
- redb.setCheckable(True)
- redb.move(10, 10)
创建一个开关按钮,我们创建一个QPushButton并让他通过调用setCheckable()方法变为可检测的.
redb.clicked[bool].connect(self.setColor)
我们将clicked信号连接到我们自己定义的方法上.我们用clicked信号来操作值的真假.
source = self.sender()
我们通过开关得到按钮的值.
if source.text() == "Red":
self.col.setRed(val)
如果红色按钮被按下,我们会对应地把背景色改为红色.
self.square.setStyleSheet("QFrame { background-color: %s }" %
self.col.name())
我们通过样式表来改变背景色.
图片:开关按钮
QSlider
一个QSlider是一个简单的把手的部件.这个把手可以向前或向后拉动.这可以为指定的任务选择值.有时候用一个滑动块比用一个选值框去输入一个数值要方便多了.
在这个例子中,我们会看到一个滑动块和一个标签.这个标签会显示为一个图像.滑动块会控制标签.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows a QSlider widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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('mute.png'))
- 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('mute.png'))
- elif value > 0 and value <= 30:
- self.label.setPixmap(QPixmap('min.png'))
- elif value > 30 and value < 80:
- self.label.setPixmap(QPixmap('med.png'))
- else:
- self.label.setPixmap(QPixmap('max.png'))
-
-
- if __name__ == '__main__':
-
- app = QApplication(sys.argv)
- ex = Example()
- sys.exit(app.exec_())
在这个例子中,我们模拟了一个音量控制.通过滑动滑动块的把手,我们可以改变标签上的图像.
- sld = QSlider(Qt.Horizontal, self)
在这里我们创建了一个水平的QSlider.
- 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'))
- ...
基于滑动条的值,我们设置标签对应的图片.在上面的代码,如果滑动条的值为零而图片是mute.png
图片:滑动条
QProgressBar
一个进度条部件是当我们处理冗长任务时适合使用的.它很形象所以用户知道了处理的进度.在PyQt5中的QProgressBar部件提供了一个水平或垂直的进度条.程序可以设置进度条的最小和最大值.默认的值是0到99.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows a QProgressBar widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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()
要激活这个进度条,我们使用一个计时器对象.
self.timer.start(100, self)
要开始计时事件,我们调用start()方法.这个方法有两个参数:超时和接收事件的对象.
- 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提供了一个基于日历的月部件.它允许用户用一个简单和直便的方式选择日期.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows a QCalendarWidget widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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)
如果我们从部件里选择一个日期,一个clicked[QDate]信号被发送.我们连接这个信号到自定义的showDate方法.
def showDate(self, date):
self.lbl.setText(date.toString())
我们通过selectedDate()方法得到选择的日期.然后我们传送日期对象到字符串并设置它到标签部件.
在此我们会继续介绍PyQt5的部件.我们会介绍QPixmap、QLineEdit、QSplitter和QComboBox.
QPixmap
一个QPixmap是一个用于处理图像的部件.它更适合在屏幕上显示图片.在下面的例子中,我们会用QPixmap来在窗口上显示一个图片.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- In this example, we dispay an image
- on the window.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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("redrock.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("redrock.png")
我们创建一个QPixmap对象,他用文件名作为参数.
lbl = QLabel(self)
lbl.setPixmap(pixmap)
我们把Pixmap放到QLabel部件里.
QLineEdit
一个QLineEdit是一个允许输入和编辑一行纯文件的部件.部件支持撤销和重做、剪切和剪贴、拖拽功能.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows text which
- is entered in a QLineEdit
- in a QLabel widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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_())
这个例子我们显示了一个行编辑器和一个标签.我们在行编辑器里输入的文本会立刻在标签中显示.
qle = QLineEdit(self)
创建一个QLineEdit部件.
qle.textChanged[str].connect(self.onChanged)
如果在行编辑器里的文本有变动,我们就调用onChanged()方法.
- def onChanged(self, text):
-
- self.lbl.setText(text)
- self.lbl.adjustSize()
在onChanged()方法里,我们设置被输入的文本到标签部件.我们通过调用adjustSize()方法来调整标签的大小到文本的长度.
图片:行编辑器
QSplitter
一个QSplitter可以让用户通过拖拽子部件的边界来控制子部件的大小.在下面的例子中,我们显示三个QFrame部件用两个分割器调整.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows
- how to use QSplitter widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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_())
在这个例子中,有三个框架和两个分割器.请注意,在某些主题下,分割器可能并不会很友好的显示.
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)
我们还可以把一个分割器加到另一个分割器部件里.
图片:分割器
QComboBox
QComboBox是一个允许用户从一个选项列表里选择的部件.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This example shows how to use
- a QComboBox widget.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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和一个标签.下拉列表框是一个有着5个选项的列表.它们是Linux发行版的名字.标签部件显示了从下拉列表框选择的选项.
combo = QComboBox(self)
combo.addItem("Ubuntu")
combo.addItem("Mandriva")
combo.addItem("Fedora")
combo.addItem("Arch")
combo.addItem("Gentoo")
我们创建一个有着5个选项的QComboBox部件.
combo.activated[str].connect(self.onActivated)
基于选择,我们调用onActivated()方法.
def onActivated(self, text):
self.lbl.setText(text)
self.lbl.adjustSize()
在方法里,我们设置被选择的文本到标签部件.我们会调整标签的大小.
图片:下拉列表框
在这部分我们会讨论拖放操作.
在计算机的图形用户界面,拖放是一个通过点击住某虚拟对象并拖动它到不同位置或其他虚拟对象的操作.通常,它可以用于调用很多种动作或在两个抽象对象之间创建不同类型的关联.
拖放是图形用户界面的一部分.拖放操作允许用户很直观地完成复杂的事情.
通常情况下,我们可以拖放两种东西:数据和一些图形对象.如果我们把一张图片从一个程序拖放到另一个程序上,那我们拖放的是二进制数据.如果我们在Firefox里拖一个标签,然后把它放到别的地方,那么我们拖放的是一个图形组件.
简单的拖放
在第一个例子里,我们有一个QLineEdit和一个QPushButton.我们把纯文本从行编辑器部件拖放到按钮部件上.按钮的标签会改变.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- This is a simple drag and
- drop example.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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部件,我们必须重装一些方法.因此,我们创建自己的Button类,它是从QPushButton类继承下来的.
self.setAcceptDrops(True)
我们让部件允许拖放事件.
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()方法,我们定义了拖放事件.因此我们可以改变按钮部件上的文本.
edit = QLineEdit('', self)
edit.setDragEnabled(True)
QLineEdit部件有一个内置的拖放操作.我们所需要做的就是调用setDragEnable()方法或激活它.
图片:一个简单的拖放
拖放一个按钮部件
在下面这个例子中,我们会演示如何拖放一个按钮部件.
- #!/usr/bin/python3
- # -*- coding: utf-8 -*-
-
- """
- ZetCode PyQt5 tutorial
-
- In this program, we can press on a button with
- a left mouse click or drag and drop the button
- with the right mouse click.
-
- author: Jan Bodnar
- website: zetcode.com
- last edited: January 2015
- """
-
- 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)
我们创建一个Button类,它继承于QPushButton.我们还重载了两个它的方法: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)
拖放对象的start()方法开始拖放操作.
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()
我们指定了拖放操作的类型.在这里是一个移动操作.
pyqt 在Widgets中显示图片和文字
- 思路非常简单:
创建window,设置窗口大小,创建label1,导入图片,创建label2,导入文字,show,结束!
-
- import sys
- from PyQt5 import QtWidgets,QtGui
-
- def window():
-
- app=QtWidgets.QApplication(sys.argv)
-
- w=QtWidgets.QWidget()
-
- w.setGeometry(100,100,300,200)
-
- w.setWindowTitle('lesson 2')
-
- l1=QtWidgets.QLabel(w)
-
- png=QtGui.QPixmap('/home/capture/Pictures/Selection_026.png')
-
- l1.setPixmap(png)
-
-
- l2=QtWidgets.QLabel(w)
-
- file=open('/home/capture/eric6_test/auto_k2_all/test1.log')
- file_text=file.read()
-
- l2.setText(file_text)
-
-
- l1.move(100,20)
- l2.move(140,120)
-
- w.show()
-
- app.exit(app.exec_())
-
- window()
不过,这样写的目的是什么,弄一个函数,来生成一个图像,没有参数可以输入?还不如不用函数呢。所以,我改了一下。
- import sys
- from PyQt5 import QtWidgets,QtGui
-
- def window(png,file_text):
-
- w=QtWidgets.QWidget()
-
- w.setGeometry(100,100,300,200)
-
- w.setWindowTitle('lesson 2')
-
- l1=QtWidgets.QLabel(w)
-
-
-
- l1.setPixmap(png)
-
-
- l2=QtWidgets.QLabel(w)
-
-
-
- l2.setText(file_text)
-
-
- l1.move(100,20)
- l2.move(140,120)
-
- w.show()
-
- sys.exit(app.exec_())
-
- if __name__ == '__main__':
-
- app = QtWidgets.QApplication(sys.argv)
-
- Png=QtGui.QPixmap('/home/capture/Pictures/Selection_026.png')
- File = open('/home/capture/eric6_test/auto_k2_all/test1.log')
- File_text = File.read()
-
- window(Png,File_text)
-
- app.exit(app.exec_())
设置按钮颜色
btn.setAutoFillBackground(True);
pal = btn.palette();
pal.setColor(QPalette.ButtonText, QColor(randint(1, 255), randint(1, 255), randint(1, 255)));
pal.setColor(QPalette.Button, QColor(randint(1,255), randint(1,255), randint(1,255)));
btn.setPalette(pal);
-
使用Designer设计Ui文件
这里设计了一个简单的登陆窗口,文件名为Login.ui,如图:
1.2、uic处理ui文件
在安装的PyQt5中有uic工具,在%PythonRoot%Python36/Scripts/pyuic5.exe。在命令行中输入如下命令
pyuic5.exe -o ui_Login.py Login.ui
处理完成后会在同目录下生成问价ui_Login.py。这个文件和上面说到的c++头文件ui_xxx.h是一致的,只不过是Python的版本而已。
1.3、编写使用ui_Login.py的Pythone文件Login.py
文件Login.py如下
-
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QDialog
from ui_Login import Ui_Login
class Login(QDialog):
"""登录窗口"""
def __init__(self, parent = None):
super(Login, self).__init__(parent)
self.ui = Ui_Login()
self.ui.setupUi(self)
self.ui.labelTips.hide()
# 连接槽函数登录按钮
self.ui.pushButtonOK.clicked.connect(self.slotLogin)
# 连接槽函数退出按钮
self.ui.pushButtonCancle.clicked.connect(self.slotCancle)
def slotLogin(self):
if self.ui.lineEditUser.text() != "admin" or self.ui.lineEditPasswd.text() != "123456":
self.ui.labelTips.show()
self.ui.labelTips.setText("用户名或密码错误!")
else:
self.accept()
def slotCancle(self):
self.reject()
:
文件Login.py完成了界面的逻辑功能的编写,连接了处理登录和退出的槽函数。
1.4、测试例子demo.py,完成调用ui的操作
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication
import Login
app = QApplication(sys.argv)
login = Login.Login()
if login.exec():
print("登录成功")
else:
print("登录退出")
sys.exit(app.exec())
以上,就是代码的整个部分,运行后出现如下界面。
1.5、总结和uic实用小技巧
回顾来看,一个ui文件应该是对应一组文件的:
- Login.ui
- ui_Login.py
- Login.py
对于这个简单的例子程序来说,这样做有点小题大做,杀鸡用牛刀了,但是对于大一点的项目来说,应该还算是比较合理的。值得一提的是,这种方式处理后,我们的代码可以不依赖与Login.ui文件了,即时你把ui文件删除了,代码正常运行。当然,随之而来就是我们修改玩ui文件后,必须再次进行uic的操作,否则你修改的ui无法在程序中响应。
对于一个一个ui文件的处理我是深恶痛绝的,因此有了下面的一个小技巧,使用批处理的方式进行uic:
@echo off
::查找目录下的所有ui文件
::当前目录
set root=%cd%
call:uicFile %root%
::子目录
for /f %%a in ('dir /ad/b') do (
call:uicFile %root%\%%a\
)
pause
::查找给定目录下的文件
:uicFile
for %%a in (%~1*.ui) do (
::使用pyuic5处理ui文件为py文件,格式按照Qt的默认格式ui_XXX.py
pyuic5.exe -o %~1ui_%%~na.py %%a
echo build: %%a
echo ==^>^> %~1ui_%%~na.py
echo.
)
goto:eof
这个批处理,完成了查找当前目录和其子目录下的所有ui文件,并自己处理成ui_xxx.py的方式。顿时感觉轻松了不少。
二、Python的方式
看完上面的例子,是不是感觉qt的ui文件使用还是比较复杂的,是的,本人也有同感,虽然之前一直都是这么用的,但是那是visual studio自己帮我完成了,我根本不需要去理会这些细节的东西。那么问题来了,PyQt5可能帮我处理这些事情吗?于是我抱着问题,去查看了PyQt5.8.2的源码,有了意外的发现,还真的有!!!
经查找,pyqt5中有一个模块叫做PyQt5.uic,里面有一个方法叫做loadUi就是专门做这个的。以下,还是使用上面的小例子作为描述。
2.1 使用Designer设计Ui文件
这里还是使用1.1中的UI文件
2.2 使用UI文件
这一步就是1.2中使用uic方式不一样了,我们将不主动进行uic处理,而是选择pyqt5替我们进行。代码如下
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QDialog
#重点和秘诀就在这里,大家注意看
from PyQt5.uic import loadUi
class Login(QDialog):
"""登录窗口"""
def __init__(self, *args):
super(Login, self).__init__(*args)
loadUi('Login.ui', self) #看到没,瞪大眼睛看
self.labelTips.hide()
self.pushButtonOK.clicked.connect(self.slotLogin)
self.pushButtonCancle.clicked.connect(self.slotCancle)
def slotLogin(self):
if self.lineEditUser.text() != "admin" or self.lineEditPasswd.text() != "123456":
self.labelTips.show()
self.labelTips.setText("用户名或密码错误!")
else:
self.accept()
def slotCancle(self):
self.reject()
2.3 测试例子demo.py
测试例子和1.4中的demo.py完全一模一样,所以不再列出。运行效果也是一样的,我又截了一张图,省的大家伙觉得是上面的结果:
pyinstaller打包pyqt5问题解决
pyinstaller打包使用pyqt5模块的时候,在win平台下,由于pyinstaller无法准确获取QT动态库文件路径,会报错导致无法打开运行程序,并提示错误信息pyinstaller failed to execute script pyi_rth_qt5plugins
此时我们需要在打包的时候直接告诉pyinstaller到哪里去找,这个路径分隔符需要是unix形式:
pyinstaller --paths C:/****/Python/Python35-32/Lib/site-packages/PyQt5/Qt/bin -F -w ****.py