必须使用两个类:QApplication和QWidget,都在PyQt5.QtWidgets包下面。
import sys
from PyQt5.QtWidgets import QApplication, QWidget
if __name__ == '__main__':
# 创建QApplication的实例
app = QApplication(sys.argv)
# 创建一个窗口
w = QWidget()
# 设置窗口的尺寸
w.resize(300, 200)
# 设置窗口的位置
w.move(300, 300)
# 设置窗口的标题
w.setWindowTitle('Hello World')
# 显示窗口
w.show()
# 进入程序的主循环,并通过exit函数保证安全退出
sys.exit(app.exec_())
在PC的设置中添加external tools,找到Anaconda下Designer.exe的路径并设置工作路径为$ProjectFileDir$
打开PC->tools->external tools->Qt Designer(你自己命名的),新建Main Window或者其他窗口
在窗体菜单中可以预览和查看代码,在属性编辑器中可以查看和修改属性
将UI文件保存到项目目录中
方法一:python -m PyQt5.uic.pyuic demo.ui -o demo.py
方法二:直接调用pyuic5
将命令添加在扩展工具中:
Program:python的全路径
Arguments:-m PyQt5.uic.pyuic $FileName$ -o $FileNameWithoutExtension$.py
Working Directory:$FileDir$
水平布局、垂直布局、栅格布局、表单布局
选中多个组件然后右键选择布局,或者先选择一种布局,然后将其他组件添加到布局之中。
可以设置容器的布局类型,也可将布局变形为容器。
绝对布局的位置与宽高都是固定的,不会随着父容器的变化而变化。
sizeHint(期望尺寸):控件的建议尺寸,一般不能被修改,每种控件都有特定的期望尺寸
self.pushButton.sizeHint().width()
self.pushButton.sizeHint().height()
最小期望尺寸:控件建议的最小尺寸,一般不能被修改
self.pushButton.minimumSize().width()
self.pushButton.minimumSize().height()
水平策略/垂直策略
水平/垂直伸展:组件宽度/高度占总宽度/高度的权重
快捷键、热键
添加热键的方法:在标签的内容中加入“&A”,“&B”,“&C”……
选择工具栏的编辑伙伴,拖动鼠标从组件A到组件B
self.label1.setBuddy(self.textEdit1)
工具栏切换到编辑Tab顺序,逐个点击控件,或者右键打开制表符顺序列表
信号:事件源发出的消息
槽:接收信号的方法
信号可以理解为事件,槽可以理解为事件处理函数
一个信号可以与多个槽绑定
绑定信号与槽的方法:切换到编辑信号/槽模式,拖动鼠标从控件A到控件B,然后配置连接,左边框选择信号,右边框选择槽。
范例1——鼠标控制窗口关闭:拖动鼠标从按钮到空白处,选择单击信号和关闭窗口的槽
范例2——复选框控制文本输入行的显示与隐藏:拖动鼠标从复选框到文本输入行,信号选择toggled(bool),槽选择setVisible(bool)
范例3——复选框控制文本输入行可用不可用:拖动鼠标从复选框到文本输入行,信号选择toggled(bool),槽选择setEnable(bool)
动作编辑器:不能把按钮拖到菜单上,使用动作编辑器创建一个动作然后拖动到菜单上
工具条与菜单的操作是类似的,并且工具栏和菜单栏可以共用action
在信号与槽编辑器中可以为action的信号绑定槽
三种窗口:
QWidget:窗口类的基类
QMainWindow:基于单文档,可以包含菜单栏、工具栏、状态栏、标题栏,是最常见的窗口形式
QDialog:基于对话框。没有菜单栏、工具栏、状态栏
直接使用源代码创建一个窗口,并在状态栏显示一个消息5秒钟:
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
class MainWin(QMainWindow):
def __init__(self):
super(MainWin, self).__init__()
# 设置窗口的标题
self.setWindowTitle('hello')
# 设置窗口的尺寸
self.resize(400, 300)
self.status = self.statusBar()
self.status.showMessage('只存在5秒的消息', 5000)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWin()
w.show()
sys.exit(app.exec_())
计算窗口的左上角点的位置——通过QDesktopWidget得到屏幕的尺寸
screen = QDesktopWidget().screenGeometry()
size = self.geometry()
newleft = (screen.width() - size.width()) / 2
newtop = (screen.height() - size.height()) / 2
self.move(int(newleft), int(newtop))
class MainWin(QMainWindow):
def __init__(self):
super(MainWin, self).__init__()
self.resize(300, 120)
self.setWindowTitle('退出应用程序')
# 添加Button
self.button1 = QPushButton('退出应用程序')
# 将信号与槽关联
self.button1.clicked.connect(self.on_button_click)
layout = QHBoxLayout()
layout.addWidget(self.button1)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
# 按钮单击事件的方法(自定义的一个槽)
def on_button_click(self):
sender = self.sender()
print(sender.text() + '按钮被按下')
app = QApplication.instance()
# 退出应用程序
app.quit()
屏幕左上角为屏幕坐标系的原点,主窗口的位置是相对屏幕坐标系的原点而言的,主窗口的尺寸包括宽度和高度。对于同一个窗口有三种不同的位置与尺寸,需要加以区别。
# 客户区的位置与尺寸,不包括标题栏和边框
x1 = widget.geometry().x()
y1 = widget.geometry().y()
w1 = widget.geometry().width()
h1 = widget.geometry().height()
# 整个框架的位置与尺寸,包括客户区、标题栏、边框
x2 = widget.frameGeometry().x()
y2 = wdiget.frameGeometry().y()
w2 = widget.frameGeometry().width()
h2 = widget.frameGeometry().height()
# 直接调用
x3 = widget.x() # 同widget.geometry().x()
y3 = widget.y() # 同widget.geometry().y()
w3 = widget.width() # 同widget.frameGeometry().width()
h3 = widget.height() # 同widget.frameGeometry().height()
注意,widget.resize()方法设置的是客户区的尺寸。
mainWin.setWindowIcon(QIcon("./icon.ico"))
这个方法用于设置窗口的图标,在windows/linux下可用,在mac os下不可用
app.setWindowIcon(QIcon("./icon.ico"))
这个方法用于设置应用程序的图标,同时会调用窗口的setWindowIcon
方法
鼠标悬停时会弹出提示信息,可以为窗口或者控件添加提示消息,提示消息可以是富文本
QToolTip.setFont(QFont("SansSerif", 12))
mainWin.setToolTip("今天是2019年10月27日")
QLabel控件常见的方法
setAligment() # 设置文本的对齐方式
setIndent() # 设置文本缩进
text() # 获取文本内容
setBuddy() # 设置伙伴关系
setText() # 设置文本内容
selectedText() # 返回所选择的字符
setWordWrap() # 设置是否允许换行
...
QLabel常见的信号
linkHovered # 当鼠标滑过QLabel控件时触发
linkActivated # 当鼠标单击QLabel控件时触发
QLabel控件的使用方法
import sys
# 注意不要把把包导错了
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPalette, QPixmap
from PyQt5.QtWidgets import QApplication, QDesktopWidget, QLabel, QVBoxLayout, QWidget
class MainWin(QWidget):
def __init__(self):
super(MainWin, self).__init__()
self.initUI()
def initUI(self):
# 设置窗口标题
self.setWindowTitle("Hello World!")
# 设置窗口居中
desktop_geometry = QDesktopWidget().screenGeometry()
self.resize(600, 400)
self.move(int((desktop_geometry.width() - self.width()) / 2),
int((desktop_geometry.height() - self.height()) / 2))
# QLabel控件的使用
# 创建一个含有4个QLabel对象的列表
labels = [QLabel(self) for _ in range(4)]
# 设置第一个标签为文字标签,背景颜色为蓝色,居中放置
labels[0].setText("这是一个文本标签")
labels[0].setAutoFillBackground(True)
palette = QPalette()
palette.setColor(QPalette.Window, Qt.blue)
labels[0].setPalette(palette)
labels[0].setAlignment(Qt.AlignCenter)
# 设置第二个标签为超链接标签
labels[1].setText("欢迎使用python gui程序")
# 设置第三个标签为图片标签,居中放置
labels[2].setAlignment(Qt.AlignCenter)
labels[2].setToolTip('这是一个图片标签')
labels[2].setPixmap(QPixmap("../resources/images/misakamikoto.jpeg"))
# 设置第四个标签为超链接标签
labels[3].setText("哔哩哔哩弹幕视频网")
labels[3].setAlignment(Qt.AlignLeft)
labels[3].setToolTip("这是一个超级链接")
# 设置打开外部链接,如果设置为True则打开外部链接,如果设置为False则执行槽函数
# labels[3].setOpenExternalLinks(True)
# 创建一个垂直布局,并将四个标签都加入到垂直布局之中
vbox = QVBoxLayout()
for l in labels:
vbox.addWidget(l)
# 给第二个标签和第四个标签配置信号与槽
labels[1].linkHovered.connect(self.linkHovered)
labels[3].linkActivated.connect(self.linkClicked)
# 将Widget的布局设置为vbox
self.setLayout(vbox)
# 鼠标悬停信号的槽
def linkHovered(self):
print("鼠标指针滑过label2")
# 鼠标单击信号的槽
def linkClicked(self):
print("单击了label4")
if __name__ == '__main__':
app = QApplication(sys.argv) # 创建一个应用程序
w = MainWin() # 创建一个窗口
w.show() # 显示窗口
sys.exit(app.exec_()) # 执行程序并安全退出
伙伴控件:可以为标签组件设置伙伴控件,从而实现诸如热键等功能。
class MainDialog(QDialog):
def __init__(self):
super(MainDialog, self).__init__()
self.initUI()
def initUI(self):
# 创建name标签和文本输入框并设置伙伴关系
nameLabel = QLabel("&Name", self)
nameLineEdit = QLineEdit(self)
nameLabel.setBuddy(nameLineEdit)
# 创建password标签和文本输入框并设置伙伴关系
passwordLabel = QLabel("&Password", self)
passwordLineEdit = QLineEdit(self)
passwordLabel.setBuddy(passwordLineEdit)
# 创建OK和CANCEL按钮并绑定至对应的槽函数
self.btnOK = QPushButton("&OK", self)
self.btnCancel = QPushButton("&Cancel", self)
self.btnOK.clicked.connect(self.on_button_ok__clicked)
self.btnCancel.clicked.connect(self.on_button_cancel_clicked)
# 创建栅格布局并添加组件
mainLayout = QGridLayout(self)
mainLayout.addWidget(nameLabel, 0, 0) # 0行0列
mainLayout.addWidget(nameLineEdit, 0, 1, 1, 2) # 0行1列起始,跨2列
mainLayout.addWidget(passwordLabel, 1, 0) # 1行0列
mainLayout.addWidget(passwordLineEdit, 1, 1, 1, 2) # 1行1列起始,跨2列
mainLayout.addWidget(self.btnOK, 2, 1) # 2行1列
mainLayout.addWidget(self.btnCancel, 2, 2) # 2行2列
self.setLayout(mainLayout) # 将栅格布局设置为对话框的布局
# OK按钮单击事件槽函数
def on_button_ok__clicked(self):
print("OK")
# CANCEL按钮单击事件槽函数
def on_button_cancel_clicked(self):
print("CANCEL")
4种回显模式
范例:
class MainDialog(QDialog):
def __init__(self):
super(MainDialog, self).__init__()
self.initUI()
def initUI(self):
# 创建一个表单布局
mainLayout = QFormLayout(self)
# 创建4个文本输入框
lineEdits = [QLineEdit(self) for _ in range(4)]
# 对文本输入框的描述
descriptions = ["Normal", "NoEcho", "Password", "PasswordEchoOnEdit"]
# 文本输入框的模式
modes = [QLineEdit.Normal, QLineEdit.NoEcho, QLineEdit.Password, QLineEdit.PasswordEchoOnEdit]
for i, j, k in zip(descriptions, lineEdits, modes):
j.setPlaceholderText(i) # 设置文本输入框的占位符
j.setEchoMode(k) # 设置文本输入框的回显模式
mainLayout.addRow(i, j) # 将文本输入框加入到表单布局中
# 设置对话框布局为表单布局
self.setLayout(mainLayout)
校验器的功能:限制文本输入框的输入
常见的校验器:
class MainDialog(QDialog):
def __init__(self):
super(MainDialog, self).__init__()
self.initUI()
def initUI(self):
mainLayout = QFormLayout(self)
lineEdits = [QLineEdit(self) for _ in range(3)]
validators = [QIntValidator(self), QDoubleValidator(self), QRegExpValidator(self)]
descriptions = ["整型", "浮点型", "数字和字母"]
validators[0].setRange(1, 99) # 设置整型校验器的范围
validators[1].setRange(-360, 360) # 设置浮点型校验器的范围
validators[1].setNotation(QDoubleValidator.StandardNotation) # 设置标准的小数表示法
validators[1].setDecimals(2) # 设置小数位数
validators[2].setRegExp(QRegExp("[a-zA-Z0-9]+$")) # 创建正则表达式并与正则表达式校验器绑定
for lineEdit, description, validator in zip(lineEdits, descriptions, validators):
mainLayout.addRow(description, lineEdit) # 添加组件到表单布局
lineEdit.setPlaceholderText(description) # 添加占位符
lineEdit.setValidator(validator) # 设置校验器
self.setLayout(mainLayout)
必须输入 | 允许输入但非必须 | 字符集/功能 |
---|---|---|
A | a | [A-Za-z] |
N | n | [A-Za-z0-9] |
X | x | 任意字符 |
9 | 0 | [0-9] |
D | d | [1-9] |
# | [1-9\+\-] | |
H | h | [A-Fa-f0-9] |
B | b | [0-1] |
> | 所有字母都大写 | |
< | 所有字母都小写 | |
! | 关闭大小写转换 | |
\ | 转义字符 |
使用:lineEdit.setInputMask(掩码)
范例:
"000.000.000.000;_" # 只能输入ip地址,没有输入的字符用_代替
"HH:HH:HH:HH:HH:HH;_" # 只能输入Mac地址,没有输入的字符用_代替
"0000-00-00" # 只能输入日期
">AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;#" # 只能输入序列号,没有输入的字符用#代替,并且将小写转换为大写
setMaxLength(4)
setAlignment(Qt.AlignRight)
setFont(QFont("Arial",20))
setReadOnly(True)
textChanged
信号QTextEdit
setPlainText("hello world")
setHtml("hello world")
toPlainText()
toHtml()
基类:QAbstractButton
派生类:QPushButton
、QToolButton
、QRadioButton
、QCheckBox
QPushButton的其它方法:
text()
:获取文本内容
setCheckable()
:设置按钮不自动弹起,可以被check
toggle()
:按钮被设置为可以check后,按下按钮
setIcon()
:设置按钮的图标
setEnabled()
:设置按钮可用
setDefault()
:设置默认按钮
信号与槽连接的另一种方式:
# 要想获得信号的发送者可以用sender()
# 或者直接传入参数,利用lambda表达式
self.button1.clicked.connect(lambda:self.whichButton(self.button1))
# 槽函数为 def whichButton(self, btn)
一个信号可以绑定多个槽函数,按照定义的先后顺序执行
QRadioButton
常用信号/方法:
setChecked()
:设置是否选中isChecked()
:返回是否被选中toggled
:选中状态变化信号QCheckBox
三种状态:未选中(0)、半选中(1)、选中(2)
常用的信号/方法:
text()
:取得文本内容setChecked()
:设置是否选中setTristate()
:设置开启半选中状态setCheckState()
:设置选中状态,三种状态为Qt.Unchecked
、Qt.PartiallyChecked
、Qt.Checked
stateChanged
:状态改变信号isChecked()
:判断是否被选中checkState()
:返回选中状态QComboBox
常用信号/方法:
addItem(item)
:添加列表项目addItems(items:list)
:添加多个列表项目currentIndexChanged(self, i)
:列表选中项改变信号,传入参数i为选中项的下标currentText()
:选中项的文本内容count
():列表项的个数itemText(i)
:列表项的文本内容水平滑块:QSlider(Qt.Horizontal)
垂直滑块:QSlider(Qt.Vertical)
常用信号/方法:
setMinimum(min)
:设置最小值setMaximum(max)
:设置最大值setSingleStep(step)
:设置步长setValue(val)
:设置滑块值setTickPostion(pos)
:设置刻度的位置,pos可以是QSlider.TicksAbove
(QSlider.TicksLeft
)、QSlider.TicksBelow
(QSlider.TicksRight
)、QSlider.TicksBothSides
setTickInterval(interval)
:设置刻度的间隔valueChanged(self)
:滑块值变化信号value()
:返回滑块的值QSpinBox
常见的信号/方法:
valueChanged(self)
value()
setValue(val)
textFromValue()
setRange(min, max)
setMaximum(max)
setMinimum(min)
setSingleStep(step)
基类:QDialog
派生类:QMessageBox
、QColorDialog
、QFileDialog
、QFontDialog
、QInputDialog
常用信号/方法:
close()
setWindowTitle(title)
setWindowModality(modality)
,设置窗口模态类型,modality可以是Qt.NonModal
、Qt.WindowModal
、Qt.ApplicationModal
种类:关于对话框、错误对话框、警告对话框、提问对话框、消息对话框
QMessageBox.about(self,'关于','这是一个关于对话框')
reply = QMessageBox.information(self,'消息','这是一个消息对话框',QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)
reply = QMessageBox.warning(self,'警告','这是一个警告对话框',QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)
reply = QMessageBox.critical(self,'错误','这是一个错误对话框',QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)
reply = QMessageBox.question(self,'提问','这是一个提问对话框',QMessageBox.Yes|QMessageBox.No,QMessageBox.Yes)
QInputDialog
items = ("C","C++","Ruby","Python","Java")
item, ok = QInputDialog.getItem(self,"请选择编程语言","语言列表",items)
text, ok = QInputDialog.getText(self,"文本输入框","输入姓名",items)
num, ok = QInputDialog.getInt(self,"整数输入框","输入数字",items)
QFontDialog
、QColorDialog
、QFileDialog
font, ok = QFontDialog.getFont()
color = QColorDialog.getColor()
fname, cancel = QFileDialog.getOpenFileName(self,"打开文件",".","图像文件(*.jpg *.png)")
dialog = QFileDialog()
dialog.setFileMode(QFileDialog.AnyFile)
dialog.setFilter(QDir.Files)
if dialog.exec():
filenames = dialog.selectedFiles()
绘图API:1、文本;2、各种图形;3、图像;
QPainter
painter = QPainter() # 创建绘图上下文
painter.begin() # 开始绘图
painter.drawText(...) # 绘图过程
painter.end() # 结束绘图
创建绘图上下文:必须在paintEvent事件方法中绘制各种元素
painter = QPainter(self)
painter.begin(self)
painter.setPen(QColor(150,43,5))
painter.setFont(QFont('SimSun',25))
painter.drawText(event.rect(),Qt.AlignCenter,self,text)
painter.end()
drawPoint(x,y)
drawLine(x0,y0,x1,y1)
drawLine(l)
绘制不同类型的直线:
pen = QPen(Qt.Red, 3, Qt.SolidLine)
painter.setPen(pen)
painter.drawLine(20,40,250,40)
pen.setStyle(Qt.DashLine)
painter.setPen(pen)
painter.drawLine(20,80,250,80)
pen.setStyle(Qt.DashDotDotLine)
painter.setPen(pen)
painter.drawLine(20,120,250,120)
pen.setStyle(Qt.DotLine)
painter.setPen(pen)
painter.drawLine(20,160,250,160)
pen.setStyle(Qt.CustomDashLine)
pen.setDashPattern([1,10,5,4])
painter.setPen(pen)
painter.drawLine(20,200,250,200)
painter = QPainter(self)
painter.begin(self)
# 设置画笔和字体
painter.setPen(QColor(255, 0, 0))
painter.setFont(QFont('SimSun', 25))
# 绘制弧
rect = QRect(0, 10, 100, 100)
painter.drawArc(rect, 0, 50 * 16)
# 绘制圆
painter.drawArc(120,0,100,100, 0, 360 * 16)
# 绘制带弦的弧
painter.drawChord(400, 400, 600, 600, 0, 130 * 16)
# 绘制扇形
painter.drawPie(10, 240, 100, 100, 12, 130*16)
# 绘制椭圆
painter.drawEllipse(120, 120, 150, 100)
# 绘制5边形
polygon = QPolygon([QPoint(140,380),QPoint(270,420),QPoint(290,512),QPoint(290,588),QPoint(200,533)])
painter.drawPolygon(polygon)
# 绘制图像
image = QImage('../resources/images/flag.jpg')
rect = QRect(10, 400, int(image.width()/3), int(image.height()/3))
painter.drawImage(rect, image)
painter.end()
brush = QBrush(Qt.SolidPattern) # 其他画刷参考源码
painter.setBrush(brush)
setAcceptDrops(True)
:设置让控件支持拖拽
setDragEnabled(True)
:设置控件可以被拖拽
dragEnterEvent(self,e)
:控件被拖动到区域内触发的事件
dropEvent(self,e)
:控件被放到区域内触发的事件
clipboard = QApplication.clipboard() # 获得剪贴板
clipboard.setText('hello world') # 设置文字
print(clipboard.text()) # 获得文字
clipboard.setPixmap(QPixmap('../resources/images/flag.jpg')) # 设置位图
image = clipboard.pixmap() # 获得位图
mimeData = QMimeData() # 设置html文本
mimeData.setHtml('Bold and Red')
clipboard.setMimeData(mimeData)
mimeData = clipboard.mimeData() # 获得html文本
if mimeData.hasHtml():
doSomething()
QCalenderWidget
setMinimumDate(QDate(1988,1,1))
:设置日期下限
setMaximumDate(QDate(2088,1,1))
:设置日期上限
setGridVisible(True)
:设置网格显示
selectedDate()
:获得选中的日期
QDateTimeEdit
QDateTimeEdit()
:无参构造函数
QDateTimeEdit(QDateTime.currentDateTime())
:有参构造函数
setDisplayFormat('yyyy-MM-dd HH:mm:ss')
:设置日期风格
setMinimumDate(date)
:设置时间下限
setMaximumDate(date)
:设置时间上限
setCalenderPopup(True)
:设置日历弹出
dateChanged(self,date)
:日期变化信号
timeChanged(self,date)
:时间改变信号
dateTimeChanged(self,date)
:日期或时间改变信号
dateTime()
:获得日期时间
minimumDate()
:获得最小日期
maximumDate()
:获得最大日期
minimumDateTime()
:获得最小日期和时间
maximumDateTime()
:获得最大日期和时间
class Menu(QMainWindow):
def __init__(self):
super(Menu, self).__init__()
self.initUI()
def initUI(self):
bar = self.menuBar() # 获得菜单栏
file = bar.addMenu('文件') # 添加菜单
file.addAction('新建') # 添加菜单项
save = QAction("保存", self) # 创建一个动作
save.setShortcut('Ctrl + S') # 设置快捷键
file.addAction(save) # 将动作添加到菜单中
quit = QAction('退出', self)
file.addAction(quit)
edit = bar.addMenu("编辑")
edit.addAction('复制')
edit.addAction('粘贴')
# 将动作的触发与槽函数绑定
save.triggered.connect(self.process)
quit.triggered.connect(self.close)
def process(self):
print(self.sender().text())
工具栏的默认只显示图标,文字只作为工具的提示
class ToolBar(QMainWindow):
def __init__(self):
super(ToolBar, self).__init__()
self.initUI()
def initUI(self):
toolbar = self.addToolBar('File') # 添加一个工具栏
new = QAction(QIcon('../resources/images/new.png'), "New", self) # 创建一个动作
toolbar.addAction(new) # 添加一个工具
open = QAction(QIcon('../resources/images/open.png'), 'Open', self)
toolbar.addAction(open)
save = QAction(QIcon('../resources/images/save.png'), 'Save', self)
toolbar.addAction(save)
toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # 设置工具栏风格为文字显示在图标的旁边
toolbar.actionTriggered.connect(self.on_action_triggered) # 为工具绑定事件
def on_action_triggered(self, a):
print(a.text())
class MyWindow(QMainWindow):
def __init__(self):
# ...
def initUI(self):
# ...
self.statusBar = QStatusBar() # 创建一个状态栏
self.setStatusBar(self.statusBar) # 设置窗口的状态栏
self.btn = QPushButton('hello world')
self.btn.clicked.connect(self.on_process_statusbar)
# ...
def on_process_statusbar(self):
self.statusBar.showMessage('hello world') # 在状态栏显示一条信息
class Menu(QMainWindow):
def __init__(self):
# ...
def initUI(self):
# ...
self.mainWidget = QWidget()
vbox = QVBoxLayout()
self.mainWidget.setLayout(vbox)
self.setCentralWidget(self.mainWidget)
# 创建一个文本输入框
self.lineEdit = QLineEdit()
vbox.addWidget(self.lineEdit)
# 创建一个按钮用于触发打印事件
self.btn1 = QPushButton('printer')
self.btn1.clicked.connect(self.on_printer)
vbox.addWidget(self.btn1)
def on_printer(self):
printer = QtPrintSupport.QPrinter() # 创建一个打印机实例
painter = QPainter() # 创建一个绘图上下文
painter.begin(printer) # 将绘图上下文与打印机实例绑定
screen = self.lineEdit.grab() # 抓取文本输入框的截图
painter.drawPixmap(10, 10, screen) #绘制位图
painter.end() # 结束绘制
# 其他方法...
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.printer = QtPrintSupport.QPrinter() # 创建一个打印机实例
self.initUI()
def initUI(self):
hb = QHBoxLayout()
vb = QVBoxLayout()
self.textEdit = QTextEdit()
hb.addWidget(self.textEdit)
widget = QWidget()
widget.setLayout(vb)
hb.addWidget(widget)
self.btnOpen = QPushButton('Open')
self.btnOpen.clicked.connect(self.on_btn_open_clicked)
vb.addWidget(self.btnOpen)
self.btnSettings = QPushButton('Settings')
self.btnSettings.clicked.connect(self.on_btn_settings_clicked)
vb.addWidget(self.btnSettings)
self.btnPrint = QPushButton('Print')
self.btnPrint.clicked.connect(self.on_btn_print_clicked)
vb.addWidget(self.btnPrint)
widget = QWidget()
widget.setLayout(hb)
self.setCentralWidget(widget)
def on_btn_open_clicked(self):
# 打开一个文件
fname = QFileDialog.getOpenFileName(self, 'open a text file', './')
if fname[0]:
with open(fname[0], 'r', encoding='utf-8', errors='ignore') as f:
self.textEdit.setText(f.read())
def on_btn_settings_clicked(self):
# 打开页面设置对话框
printDialog = QPageSetupDialog(self.printer, self)
printDialog.exec()
def on_btn_print_clicked(self):
# 打开打印对话框
printDialog = QPrintDialog(self.printer, self)
if QDialog.Accepted == printDialog.exec():
self.textEdit.print(self.printer) # QTextEdit控件可以直接调用本方法打印
QTableView
需要创建QTableView实例和一个数据源(Model),使用了MVC模式,将后端的数据与前端页面的耦合度降低
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
self.model = QStandardItemModel(4, 3) # 创建标准数据源
self.model.setHorizontalHeaderLabels(['ID', 'Name', 'Age']) # 设置列字段标签
self.tableView = QTableView() # 创建列表控件
self.tableView.setModel(self.model) # 关联QTableView控件和Model
# 添加数据
items = [[QStandardItem('10'), QStandardItem('雷神'), QStandardItem('2000')],
[QStandardItem('10'), QStandardItem('雷神'), QStandardItem('2000')],
[QStandardItem('10'), QStandardItem('雷神'), QStandardItem('2000')]]
for i in range(len(items)):
for j in range(len(items[i])):
self.model.setItem(i, j, items[i][j])
vb = QVBoxLayout()
vb.addWidget(self.tableView)
w = QWidget()
w.setLayout(vb)
self.setCentralWidget(w)
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
listView = QListView() # 控件
listModel = QStringListModel() # 数据源
self.list = ['孙悟空', '猪八戒', '沙僧', '唐僧'] # 列表
listModel.setStringList(self.list) # 将列表与数据源关联
listView.setModel(listModel) # 设置数据源
listView.clicked.connect(self.on_item_clicked) # 绑定事件
vb = QVBoxLayout()
vb.addWidget(listView)
w = QWidget()
w.setLayout(vb)
self.setCentralWidget(w)
def on_item_clicked(self, item):
QMessageBox.information(self, 'QListView', '您选择了'+self.list[item.row()])
QListWidget
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
self.listWidget = QListWidget()
l = [str(i) for i in range(0,10)]
for item in l:
self.listWidget.addItem(item) # 添加列表项
self.setCentralWidget(self.listWidget)
self.listWidget.itemClicked.connect(self.on_item_clicked) # 绑定列表项被点击的事件
def on_item_clicked(self, item):
QMessageBox.information(self, 'QListView', '您选择了'+ item.text())
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
tw = QTableWidget() # 创建
tw.setRowCount(4) # 设置行数
tw.setColumnCount(3) # 设置列数
self.setCentralWidget(tw)
tw.setHorizontalHeaderLabels(['姓名','年龄','籍贯']) # 设置行标签
info = ['小明','20','陕西']
items = [QTableWidgetItem(item) for item in info]
for i in range(len(items)):
tw.setItem(0, i, items[i]) # 设置单元格
# 禁止编辑
tw.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 整行选择
tw.setSelectionBehavior(QAbstractItemView.SelectRows)
# 调整列和行
tw.resizeColumnsToContents()
tw.resizeRowsToContents()
# 设置行标签、列标签可见性
# tw.horizontalHeader().setVisible(False)
# tw.verticalHeader().setVisible(False)
# 设置列标签
tw.setVerticalHeaderLabels(['a','b'])
# 隐藏表格线
tw.setShowGrid(False)
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
tw = QTableWidget()
tw.setRowCount(4)
tw.setColumnCount(3)
self.setCentralWidget(tw)
textItem = QTableWidgetItem('小明')
tw.setItem(0, 0, textItem) # 将文本项添加到单元格中
combox = QComboBox()
combox.addItems(['男', '女'])
combox.setStyleSheet('QComboBox{margin:3px};') # 设置qss样式
tw.setCellWidget(0, 1, combox) # 将组件添加到单元格中
btn = QPushButton('修改')
btn.setDown(True)
btn.setStyleSheet('QPushButton{margin:3px};') # 设置qss样式
tw.setCellWidget(0, 2, btn) # 将组件添加到单元格中
1、数据的定位:findItems
2、如果找到了满足条件的单元格,会定位到单元格所在的行:setSliderPosition(row)
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
tw = QTableWidget()
tw.setRowCount(40)
tw.setColumnCount(4)
self.setCentralWidget(tw)
for i in range(40):
for j in range(4):
tw.setItem(i, j, QTableWidgetItem('(%d, %d)' % (i, j)))
text = '(10, 2)'
items = tw.findItems(text, Qt.MatchExactly) # 搜索满足条件的单元格,可以选择其他匹配模式
if items:
item = items[0]
item.setBackground(QBrush(QColor(0, 255, 0)))
item.setForeground(QBrush(QColor(255, 0, 0)))
row = item.row()
# 定位到指定的行
tw.verticalScrollBar().setSliderPosition(row)
tableWidget.sortItems(2, Qt.DescendingOrder)
:按第二列降序排列
tableWidget.sortItems(2, Qt.AscendingOrder)
:按第二列升序排列
item.setFont(QFont('Times', 14, QFont.Black))
:设置字体
item.setForeground(QBrush(QColor(255, 0, 0)))
:设置前景色
item.setBackground(QBrush(QColor(0, 0, 255)))
:设置背景色
item.setTextAlignment(Qt.AlignRight|Qt.AlignBottom)
:设置右对齐和下对齐
item.setSpan(row, col, rowspan, colspan)
:单元格合并
tw.setRowHeight(1,80)
:设置第1行行高为80
tw.setColumnWidth(2,120)
:设置第2列列宽为120
tw.setItem(0, 3, QTableWidgetItem(QIcon('../resources/images/icon.png'), '文本'))
:设置图文混排的单元格
item.setIconSize(QSize(w,h))
:设置单元格图片的尺寸
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
self.tw = QTableWidget()
self.setCentralWidget(self.tw)
self.tw.setRowCount(4)
self.tw.setColumnCount(3)
for i in range(4):
for j in range(3):
self.tw.setItem(i, j, QTableWidgetItem('(%d, %d)' % (i, j)))
# 设置允许上下文菜单弹出
self.tw.setContextMenuPolicy(Qt.CustomContextMenu)
# 绑定事件
self.tw.customContextMenuRequested.connect(self.generateMenu)
def generateMenu(self, pos):
print(pos)
pos = self.tw.mapToGlobal(pos) # 映射到屏幕坐标系
for i in self.tw.selectionModel().selection().indexes():
rowNum = i.row()
# 如果选择的行索引小于2,弹出上下文菜单
if rowNum < 2:
menu = QMenu(self) # 创建上下文菜单
items = [menu.addAction(i) for i in ['hello', 'world', 'good', 'morning']]
action = menu.exec(pos) # 弹出菜单
for i in range(len(items)):
if items[i] == action:
print('选中了第%d个菜单项' % i)
break
def initUI(self):
self.tree = QTreeWidget() # 创建一个树控件
self.setCentralWidget(self.tree)
self.tree.setColumnCount(2) # 设置列数
self.tree.setHeaderLabels(['Key','Value']) # 设置列标签
self.tree.setColumnWidth(0, 120) # 设置某列列宽
root = QTreeWidgetItem(self.tree) # 创建一个子节点
root.setText(0, '根节点') # 设置子节点某列的文本值
root.setIcon(0, QIcon('./resources/images/icon.png')) # 设置子节点某列的图标
childs = []
child = QTreeWidgetItem(root)
child.setText(0, '子节点1')
child.setText(1, '子节点1的数据')
child.setIcon(0, QIcon('../resources/images/icon.png'))
childs.append(child)
child = QTreeWidgetItem(root)
child.setText(0, '子节点2')
child.setText(1, '子节点2的数据')
child.setIcon(0, QIcon('../resources/images/icon.png'))
child.setCheckState(0, Qt.Checked) # 设置子节点某列的可选
childs.append(child)
self.tree.expandAll() # 展开树
self.tree.clicked.connect(self.on_tree_clicked) # 绑定事件
# 槽函数
def on_tree_clicked(self, index):
item = self.tree.currentItem() # 获得当前项
print(index.row())
print('key = %s, value = %s' % (item.text(0), item.text(1)))
def on_btn_add_clicked(self):
item = self.tree.currentItem()
new_item = QTreeWidgetItem(item)
new_item.setText(0, '新键')
new_item.setText(1, '新值')
def on_btn_delete_clicked(self):
root = self.tree.invisibleRootItem()
for item in self.tree.selectedItems():
(item.parent() or root).removeChild(item)
def on_btn_modify_clicked(self):
item = self.tree.currentItem()
item.setText(0, '修改键')
item.setText(1, '修改值')
if __name__ == '__main__':
app = QApplication(sys.argv)
model = QDirModel()
view = QTreeView()
view.setModel(model)
view.setGeometry(100,100,600,400)
view.show()
sys.exit(app.exec_())
QTabWidget
def initUI(self):
tabWidget = QTabWidget() # 创建选项卡组件
self.setCentralWidget(tabWidget)
widgets = [QPushButton(str(i)) for i in range(3)]
for i in range(len(widgets)):
tabWidget.addTab(widgets[i], '选项卡'+str(i)) # 添加选项卡
tabWidget.setTabText(0, 'hello world') # 设置选项卡标题
QStackedWidget
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
w = QWidget()
self.setCentralWidget(w)
hbox = QHBoxLayout()
w.setLayout(hbox)
# 创建列表组件
self.listWidget = QListWidget()
self.listWidget.addItems(['a', 'b', 'c'])
self.listWidget.currentItemChanged.connect(self.onCurrentItemChanged) # 绑定事件
self.listWidget.setFixedWidth(100)
hbox.addWidget(self.listWidget)
# 创建堆栈组件
self.stackedWidget = QStackedWidget()
hbox.addWidget(self.stackedWidget)
for i in range(3):
self.stackedWidget.addWidget(QPushButton('按钮'+str(i))) # 向堆栈组件添加面板
# 槽函数
def onCurrentItemChanged(self, current, previous):
# 设置显示的面板
self.stackedWidget.setCurrentIndex(self.listWidget.row(current))
QDockWidget
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.initUI()
def initUI(self):
self.mainWidget = QWidget()
self.setCentralWidget(self.mainWidget)
self.dockWidget = QDockWidget('这是一个停靠窗口', self) # 创建一个停靠窗口
self.listWidget = QListWidget()
for i in range(3):
self.listWidget.addItems([str(i) for i in range(100)])
self.dockWidget.setWidget(self.listWidget) # 设置停靠窗口的客户区
self.dockWidget.setFloating(True) # 设置停靠窗口默认悬浮状态
self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget) # 将停靠窗口添加到主窗口上
QMdiArea
、QMdiSubWindow
class MyWin(QMainWindow):
count = 0 # 记录一下当前有多少窗口
def __init__(self):
super(MyWin, self).__init__()
self.setWindowTitle("容纳多文档的窗口")
self.mdi = QMdiArea() # 创建一个多文档窗口容器
self.setCentralWidget(self.mdi) # 将多文档窗口容器设置为中心组件
bar = self.menuBar() # 获得菜单栏的引用
file = bar.addMenu("File") # 添加一个菜单
# 添加菜单项
file.addAction("New")
file.addAction("Cascade")
file.addAction("Titled")
# 将菜单项与事件绑定
file.triggered.connect(self.window_action)
def window_action(self, q):
print(q.text())
if q.text() == "New":
MyWin.count = MyWin.count + 1
sub = QMdiSubWindow() # 添加一个子窗口
sub.setWidget(QTextEdit("这是最后的斗争")) # 设置子窗口的核心组件
sub.setWindowTitle("这是一个子窗口,编号为" + str(MyWin.count)) # 设置子窗口的标题
self.mdi.addSubWindow(sub) # 将子窗口添加到多文档窗口容器中
sub.show() # 显示子窗口
elif q.text() == "Cascade":
self.mdi.cascadeSubWindows() # 子窗口堆叠放置
elif q.text() == "Titled":
self.mdi.tileSubWindows() # 子窗口平铺放置
QScrollBar
作用:
1、通过滚动条值的变化控制其他控件状态的变化
2、通过滚动条的变化控制控件位置的变化
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
hbox = QHBoxLayout()
self.label = QLabel("拖动滚动条去改变文字颜色")
hbox.addWidget(self.label)
# 创建滚动条1
self.scroll_bar_1 = QScrollBar()
self.scroll_bar_1.setMaximum(255)
self.scroll_bar_1.sliderMoved.connect(self.on_scroll_bar_slider_moved) # 将滑动滚动条的信号绑定
# 创建滚动条2
self.scroll_bar_2 = QScrollBar()
self.scroll_bar_2.setMaximum(255)
self.scroll_bar_2.sliderMoved.connect(self.on_scroll_bar_slider_moved)
# 创建滚动条3
self.scroll_bar_3 = QScrollBar()
self.scroll_bar_3.setMaximum(255)
self.scroll_bar_3.sliderMoved.connect(self.on_scroll_bar_slider_moved)
# 创建滚动条4
self.scroll_bar_4 = QScrollBar()
self.scroll_bar_4.setMaximum(300)
self.scroll_bar_4.sliderMoved.connect(self.on_scroll_bar_4_slider_moved)
hbox.addWidget(self.scroll_bar_1)
hbox.addWidget(self.scroll_bar_2)
hbox.addWidget(self.scroll_bar_3)
hbox.addWidget(self.scroll_bar_4)
w = QWidget()
w.setLayout(hbox)
self.setCentralWidget(w)
# 使用滚动条来控制文字的颜色
def on_scroll_bar_slider_moved(self):
# 获得滚动条的值
r = self.scroll_bar_1.value()
g = self.scroll_bar_2.value()
b = self.scroll_bar_3.value()
print("color =", r, g, b)
palette = QPalette()
c = QColor(r, g, b, 255)
palette.setColor(QPalette.Foreground, c)
self.label.setPalette(palette)
# 使用滚动条来控制组件的位置
def on_scroll_bar_4_slider_moved(self):
self.label.move(self.label.x(), self.y() + self.scroll_bar_4.value())
QTimer
、QThread
多线程:用于同时完成多个任务,底层可以并发或并行
定时器:每隔一段时间会调用一次
class MyWin(QMainWindow):
def __init__(self):
super(MyWin, self).__init__()
self.setWindowTitle("Display the time dynamically")
self.label = QLabel("Display the current time")
self.start_btn = QPushButton("start")
self.end_btn = QPushButton("end")
layout = QGridLayout()
# 创建一个定时器并绑定事件
self.timer = QTimer()
self.timer.timeout.connect(self.on_timer)
layout.addWidget(self.label, 0, 0, 1, 2)
layout.addWidget(self.start_btn, 1, 0)
layout.addWidget(self.end_btn, 1, 1)
self.start_btn.clicked.connect(self.on_start_btn_clicked)
self.end_btn.clicked.connect(self.on_end_btn_clicked)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
# 绑定了timeout信号的槽函数
def on_timer(self):
time = QDateTime.currentDateTime()
time_display = time.toString("yyyy-MM-dd hh:mm:ss")
self.label.setText(time_display)
def on_start_btn_clicked(self):
# 开启定时器,并且指明每1000毫秒产生一个timeout信号
self.timer.start(1000)
self.start_btn.setEnabled(False)
self.end_btn.setEnabled(True)
def on_end_btn_clicked(self):
# 关闭定时器
self.timer.stop()
self.start_btn.setEnabled(True)
self.end_btn.setEnabled(False)
QTimer.singleShot()
:让程序只执行一次而不是周期执行
if __name__ == '__main__':
app = QApplication(sys.argv)
# w = MyWin()
# w.show()
label = QLabel("hello world! The window will be closed in 5 seconds.")
label.show()
QTimer.singleShot(5000, app.quit) # 设置5000毫秒后自动关闭
sys.exit(app.exec_())
QThread
继承QThread,实现run方法,使用sleep方法休眠若干时间
关键:pyqtSingal()
、emit()
、connect()
、run()
、start()
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
sec = 0
# 继承QThread类
class WorkThread(QThread):
timer = pyqtSignal() # 每隔一秒发送一次信号
end = pyqtSignal() # 计数完成后发送一次信号
# 实现run方法
def run(self) -> None:
while True:
self.sleep(1) # 休眠1秒
if sec == 5:
self.end.emit() # 发送end信号
break
self.timer.emit() # 发送timer信号
class Counter(QWidget):
def __init__(self):
super(Counter, self).__init__()
self.setWindowTitle("use QThread to make a counter")
self.resize(300, 120)
layout = QVBoxLayout()
self.lcdNumber = QLCDNumber()
layout.addWidget(self.lcdNumber)
button = QPushButton("start to count")
layout.addWidget(button)
self.workThread = WorkThread() # 创建一个线程的实例
# 绑定事件
self.workThread.timer.connect(self.countTime)
self.workThread.end.connect(self.end)
button.clicked.connect(self.work)
self.setLayout(layout)
def countTime(self):
global sec
sec += 1
self.lcdNumber.display(sec)
def end(self):
QMessageBox.information(self,"message","end counting",QMessageBox.Ok)
def work(self):
# 运行线程实例
self.workThread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Counter()
w.show()
sys.exit(app.exec_())
QWebEngineView
:用来显示web页面
class WebEngineView(QMainWindow):
def __init__(self):
super(WebEngineView, self).__init__()
self.setWindowTitle('open external web page example')
self.setGeometry(5, 30, 1355, 730)
self.browser = QWebEngineView()
url = os.getcwd() + '/resources/static/pages/test.html'
self.browser.load(QUrl(QUrl.fromLocalFile(url))) # 可以是外部链接
self.setCentralWidget(self.browser)
使用硬编码方式显示网页:
class WebEngineView(QMainWindow):
def __init__(self):
super(WebEngineView, self).__init__()
self.setWindowTitle('open external web page example')
self.setGeometry(5, 30, 1355, 730)
self.browser = QWebEngineView()
self.browser.setHtml("""
Test Page
Hello PyQt5
Hello PyQt5
Hello PyQt5
Hello PyQt5
""")
self.setCentralWidget(self.browser)
python调用js函数
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Pagetitle>
<script>
function get_full_name(value){
alert(value)
var firstname = document.getElementById("firstname").value;
var lastname = document.getElementById("lastname").value;
var fullname = firstname + ' ' + lastname;
document.getElementById('fullname').value = fullname;
document.getElementById('submit-btn').style.display = 'block';
return fullname;
}
script>
head>
<body>
<form>
<label>First Name: label>
<input type="text" name="firstname" id="firstname"/><br/>
<label>Last Name: label>
<input type="text" name="lastname" id="lastname"/><br/>
<label>Full Name:label>
<input type="text" name="fullname" id="fullname"/><br/>
<input style="display: none;" type="submit" id="submit-btn" />
form>
body>
html>
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import *
class WebEngineView(QMainWindow):
def __init__(self):
super(WebEngineView, self).__init__()
self.setWindowTitle('run javascript')
self.setGeometry(5, 30, 600, 400)
self.layout = QVBoxLayout()
# 创建一个Web引擎组件
self.browser = QWebEngineView()
# 加载html文件
url = os.getcwd() + "/resources/static/pages/test.html"
self.browser.load(QUrl.fromLocalFile(url))
self.layout.addWidget(self.browser)
self.button = QPushButton("set fullname")
self.button.clicked.connect(self.on_button_clicked)
self.layout.addWidget(self.button)
w = QWidget()
w.setLayout(self.layout)
self.setCentralWidget(w)
def on_button_clicked(self):
value = "hello world"
# python调用js函数,并绑定回调函数
self.browser.page().runJavaScript('get_full_name("%s");' % value, self.js_callback)
def js_callback(self, result):
print(result)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = WebEngineView()
w.show()
sys.exit(app.exec_())
js调用python:
方法:将python的一个对象映射到js中,将槽函数映射到js中
import os
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import *
# 要被映射到js的对象的类
class Factorial(QObject):
# 参数类型为int,返回值类型int
@pyqtSlot(int, result=int)
def factorial(self, n):
if n == 0 or n == 1:
return 1
else:
return self.factorial(n - 1) * n
# 定义channel
channel = QWebChannel()
# 定义被映射对象
factorial = factorial.Factorial()
class PyFactorial(QWidget):
def __init__(self):
super(PyFactorial, self).__init__()
self.setWindowTitle('Python Calculate Factorial')
self.resize(600, 300)
layout = QVBoxLayout()
self.setLayout(layout)
# 创建一个web浏览器控件
self.browser = QWebEngineView()
# 加载html文本
url = os.getcwd() + '/resources/static/pages/test.html'
self.browser.load(QUrl.fromLocalFile(url))
# 使用channel注册一个对象
channel.registerObject("obj", factorial)
# 将channel与web浏览器控件关联
self.browser.page().setWebChannel(channel)
layout.addWidget(self.browser)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = PyFactorial()
w.show()
sys.exit(app.exec_())
<html lang="en">
<head>
<meta charset="UTF-8">
<title>A demo pagetitle>
<script src="../js/qwebchannel.js">script>
<script>
function callback(result){
alert('result = ' + result);
}
// 在页面装载完成时获得python映射的对象
document.addEventListener('DOMContentLoaded', function(){
new QWebChannel(qt.webChannelTransport, function(channel){
window.obj = channel.objects.obj;
});
});
function onFactorial(){
if (window.obj) {
var n = parseInt(document.getElementById('n').value);
// 使用映射的对象
window.obj.factorial(n, callback);
}
}
script>
head>
<body>
<form>
<label>Please input N: label>
<input type="text" id="n"/><br />
<input type="button" value="calculate factorial" onclick="onFactorial()"/>
form>
body>
html>
绝对布局:通过move确定位置
水平/垂直盒布局:
hbox.addWidget(w)
hbox.setSpacing(40)
hbox.addWidget(w, 2, Qt.AlignLeft | Qt.AlignTop) # 将w添加到水平盒布局中,水平权重为2,左上对齐
hbox.addStretch(2) # 添加伸缩量,伸缩量设为0表示先排列,剩下的再按大于零伸缩量排
栅格布局:
grid.addWidget(w,i,j)
表单布局:
formLayout.addRow(label,textEdit)
QSplitter
topLeft = QFrame()
topLeft.setFrameShape(QFrame, StyledPanel)
bottom = QFrame()
bottom.setFrameShape(QFrame, StyledPanel)
splitter1 = QSplitter(Qt.Horizontal)
textEdit = QTextEdit()
splitter1.addWidget(topLeft)
splitter1.addWidget(textEdit)
splitter2 = QSplitter(Qt.Vertical)
splitter2.addWidget(splitter1)
splitter2.addWidget(bottom)
hbox = QHBoxLayout(self)
hbox.addWidget(splitter2)
self.setLayout(hbox)
自定义信号:pyqtSignal()
class MyTypeSignal(QObject):
# 定义一个信号
send_message = pyqtSignal(object)
# 定义发送三个参数的信号
send_message_1 = pyqtSignal(str,int,int)
def run(self):
self.send_message.emit('hello PyQt5') # 发送信号
def run_1(self):
self.send_message_1.emit('hello',3,4) # 发送信号
class MySlot(QObject):
# 槽函数
def get(self, msg):
print("信息:"+msg)
# 槽函数
def get_1(self,msg,a,b):
print("msg =",msg,"a =",a,"b =",b)
if __name__ == '__main__':
signal = MyTypeSignal()
slot = MySlot()
signal.send_message.connect(slot.get) # 将信号与槽进行关联
signal.run()
signal.send_message.disconnect(slot.get) # 将信号与槽断开关联
signal.run()
signal.send_message_1.connect(slot.get_1) # 将信号与槽进行关联
signal.run_1()
为类添加多个信号:
class MultiSignal(QObject):
signal_1 = pyqtSignal()
signal_2 = pyqtSignal(int)
signal_3 = pyqtSignal(int, str)
signal_4 = pyqtSignal(list)
signal_5 = pyqtSignal(dict)
# 声明一个重载版本的信号,槽函数参数可以是int和str,也可以是单独的str
signal_6 = pyqtSignal([int, str], [str])
def __init__(self):
super(MultiSignal, self).__init__()
self.signal_1.connect(self.signal_call_1)
self.signal_2.connect(self.signal_call_2)
self.signal_3.connect(self.signal_call_3)
self.signal_4.connect(self.signal_call_4)
self.signal_5.connect(self.signal_call_5)
self.signal_6[int, str].connect(self.signal_call_6) # 显式关联到第一个重载版本
# self.signal_6.connect(self.signal_call_6) # 默认关联到第一个重载版本
self.signal_6[str].connect(self.signal_call_7) # 显式关联到第二个重载版本
self.signal_1.emit()
self.signal_2.emit(10)
self.signal_3.emit(1, 'hello')
self.signal_4.emit([1, 2, 3, 4, 5, 6])
self.signal_5.emit({'name': 'bill', 'age': 30})
self.signal_6[str].emit('test') # 调用时也要显式指定参数类型
self.signal_6[int, str].emit(1, 'test') # 调用时也要显式指定参数类型
def signal_call_1(self):
print(1)
def signal_call_2(self, val):
print(2, val)
def signal_call_3(self, val, text):
print(3, val, text)
def signal_call_4(self, val):
print(4, val)
def signal_call_5(self, val):
print(5, val)
def signal_call_6(self, val, text):
print(6, val, text)
def signal_call_7(self, val):
print(7, val)
if __name__ == '__main__':
multi_signal = MultiSignal()
信号槽n对n连接与断开连接:
为一个窗口添加信号:
self.btn.clicked.connect(self.close)
多线程更新UI数据:
import os
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
# 子线程,用于更新主线程中的控件的值
class BackendThread(QThread):
update_date = pyqtSignal(str) # 创建一个信号
# 线程的主体部分
def run(self) -> None:
while True:
# 获取时间并格式化为字符串
data = QDateTime.currentDateTime()
currentTime = data.toString('yyyy-MM-dd hh:mm:ss')
self.update_date.emit(str(currentTime)) # 发送信号
time.sleep(1)
# 对话框组件类
class ThreadUpdateUI(QDialog):
def __init__(self):
QDialog.__init__(self)
self.setWindowTitle('Multiple threads to update UI data')
self.resize(400, 100)
self.input = QLineEdit(self)
self.input.resize(400, 100)
self.initUI()
def initUI(self):
self.backend = BackendThread() # 创建一个子线程实例
self.backend.update_date.connect(self.handleDisplay) # 将子线程实例的信号与槽函数进行绑定
self.backend.start() # 运行子线程实例
# 槽函数
def handleDisplay(self, data):
self.input.setText(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = ThreadUpdateUI()
w.show()
sys.exit(app.exec_())
信号与槽自动连接:
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__()
self.setWindowTitle('Add signal for the window')
self.resize(600, 400)
hbox = QHBoxLayout()
self.setLayout(hbox)
okButton = QPushButton('OK')
okButton.setObjectName('ok_button') # 设置组件的对象名
cancelButton = QPushButton('Cancel')
cancelButton.setObjectName('cancel_button') # 设置组件的对象名
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)
QMetaObject.connectSlotsByName(self) # 调用自动连接的函数
# 添加槽函数的注解
@pyqtSlot()
# 槽函数命名要符合规范,on_组件对象名_信号名
def on_ok_button_clicked(self):
QMessageBox.information(self,'info','you clicked the button ok')
# 添加槽函数的注解,on_组件对象名_信号名
@pyqtSlot()
# 槽函数命名要符合规范
def on_cancel_button_clicked(self):
QMessageBox.information(self,'info','you clicked the button cancel')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.show()
sys.exit(app.exec_())
lambda表达式:
fun_1 = lambda :print('hello world')
fun_2 = lambda x,y:print(x,y)
使用lambda表达式为槽函数传递参数:
btn.clicked.connect(lambda :self.on_btn_clicked(1,2))
btn.clicked.connect(lambda :print('hello world'))
使用Partial对象为槽函数传递参数:
btn.clicked.connect(partial(self.on_btn_clicked,10,20))
btn.clicked.connect(partial(self.on_btn_clicked,x,y))
覆盖槽函数:直接覆盖,无需连接
1、不使用信号与槽,强耦合
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MultiWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('MultiWindow Interaction: No use of signals and slots')
self.lineEdit = QLineEdit(self)
self.btn_1 = QPushButton('Popup the dialog 1')
self.btn_1.clicked.connect(self.on_btn_1_clicked)
self.btn_2 = QPushButton('Popup the dialog 2')
self.btn_2.clicked.connect(self.on_btn_2_clicked)
grid_layout = QGridLayout()
grid_layout.addWidget(self.lineEdit)
grid_layout.addWidget(self.btn_1)
grid_layout.addWidget(self.btn_2)
self.setLayout(grid_layout)
def on_btn_1_clicked(self):
dialog = DateDialog(self) # 创建DateDialog的一个实例
result = dialog.exec() # 执行对话框实例
date = dialog.dateTime() # 调用实例方法获得日期时间
self.lineEdit.setText(date.date().toString()) # 更新组件的值
dialog.destroy() # 销毁该实例
def on_btn_2_clicked(self):
date, time, result = DateDialog.getDateTime() # 调用静态方法获得日期、时间、选择结果
self.lineEdit.setText(date.toString()) # 更新组件的值
if result == QDialog.Accepted:
print('The button ok was clicked')
else:
print('The button cancel was clicked')
class DateDialog(QDialog):
def __init__(self, parent=None):
super(DateDialog, self).__init__(parent)
self.setWindowTitle('Multiple windows interaction')
layout = QVBoxLayout()
self.datetime = QDateTimeEdit(self)
self.datetime.setCalendarPopup(True)
self.datetime.setDateTime(QDateTime.currentDateTime())
layout.addWidget(self.datetime)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
self.setLayout(layout)
# 获得日期时间控件的值
def dateTime(self):
return self.datetime.dateTime()
@staticmethod
def getDateTime(parent=None):
dialog = DateDialog(parent) # 创建本类的一个实例
result = dialog.exec() # 执行该实例
date = dialog.dateTime() # 调用实例方法获得日期时间控件的值
return date.date(), date.time(), result == QDialog.Accepted # 返回日期、时间、对话框的值
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MultiWindow()
w.show()
sys.exit(app.exec_())
2、使用信号与槽
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MultiWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('MultiWindow Interaction: No use of signals and slots')
self.lineEdit = QLineEdit()
self.btn = QPushButton('open dialog')
self.btn.clicked.connect(self.on_btn_clicked)
vbox = QVBoxLayout()
vbox.addWidget(self.lineEdit)
vbox.addWidget(self.btn)
self.setLayout(vbox)
def on_btn_clicked(self):
dialog = DateDialog(self)
dialog.date_time_message.connect(self.on_date_time_message) # 绑定事件
dialog.show()
def on_date_time_message(self, dateTime: QDateTime):
self.lineEdit.setText(dateTime.toString())
class DateDialog(QDialog):
date_time_message = pyqtSignal(QDateTime)
def __init__(self, parent=None):
super(DateDialog, self).__init__(parent)
self.setWindowTitle('Multiple windows interaction')
layout = QVBoxLayout()
self.datetime = QDateTimeEdit(self)
self.datetime.setCalendarPopup(True)
self.datetime.setDateTime(QDateTime.currentDateTime())
self.datetime.dateTimeChanged.connect(self.on_date_time_changed)
layout.addWidget(self.datetime)
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
self.setLayout(layout)
def on_date_time_changed(self):
self.date_time_message.emit(self.datetime.dateTime()) # 发送事件
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MultiWindow()
w.show()
sys.exit(app.exec_())
QApplication.setStyle()
QStyleFactory.keys()
:pyqt支持的所有窗口风格
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class MainWidget(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('set window style')
self.styleLabel = QLabel('set window style')
self.styleComboBox = QComboBox()
hbox = QHBoxLayout()
self.styleComboBox.addItems(QStyleFactory.keys()) # 获取支持的风格名称列表
print(QApplication.style().objectName()) # 得到当前应用的窗口风格名称
index = self.styleComboBox.findText(QApplication.style().objectName())
self.styleComboBox.setCurrentIndex(index)
self.styleComboBox.activated[str].connect(self.handleStyleChanged) # 绑定combobox的事件
hbox.addWidget(self.styleLabel)
hbox.addWidget(self.styleComboBox)
self.setLayout(hbox)
def handleStyleChanged(self, style):
QApplication.setStyle(style) # 设置窗口组件的风格
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWidget()
w.show()
sys.exit(app.exec_())
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(600, 400)
self.setWindowTitle('set window style sheet')
self.setWindowFlags(Qt.WindowMaximizeButtonHint |Qt.WindowMinimizeButtonHint | Qt.WindowCloseButtonHint| Qt.WindowStaysOnTopHint) # 设置窗口风格
self.setObjectName('mainWindow') # 添加对象引用名称
# 注意路径的写法
self.setStyleSheet('#mainWindow{border-image:url(../resources/images/national_flag.jpg);}') # 添加样式
方法一:使用setGeometry()设置窗口尺寸
方法二:showMaximized()
、showMinimized()
1、如何绘图:在paintEvent方法中绘图,通过调用update方法更新
2、在哪里绘图:在白色背景的QPixmap对象中绘图
3、如何通过移动鼠标进行绘图:三个事件——鼠标按下、鼠标移动、鼠标弹起,使用event.button()
判断是鼠标左键还是右键,使用drawLine绘制直线
Qt样式表:用于设计控件的样式
app = QApplication(sys.argv)
w = MyWindow()
# 选择器
qssStyle = '''
QPushButton{
background-color: red
}
'''
w.setStyleSheet(qssStyle) # 为所有的button设置样式
w.show()
sys.exit(app.exec_())
QSS选择器:
# 设置属性
btn.setProperty('name','btn1')
# 选择器
'''
QPushButton[name='btn1']{
background-color: red;
color: yellow;
height: 120;
font-size: 60px;
}
'''
'''
QComboBox#myComboBox::drop-down{
images:url(../resources/iamges/dropdown.png)
}
'''
添加背景图:
'''
QLabel#label{
border-image: url(...)
}
#btn1{
border-radius: 4px;
background-image: url(...);
}
#btn1:Pressed{
background-image: url(...);
}
'''
装载QSS文件:
class CommonHelper:
@staticmethod
def readQSS(style):
with open(style, 'r') as f:
return f.read()
通过mask实现异形窗口,需要一张透明的png图,透明部分被扣出形成一个非矩形区域
self.setMask(QBitmap('./images/mask.png'))
异形窗口的移动:通过鼠标事件来实现,计算移动后左上角点的坐标即可
异形窗口的动画:定时器事件,切换图片
设置透明度:setWindowOpacity()
QMovie
、setMovie()
、movie.start()
img.scaled(w,h,Qt.IgnoreAspectRatio,Qt.SmoothTransformation)
用动画效果改变窗口组件:QPropertyAnimation
pip3 install pyinstaller
pyinstall Calc.py
pyinstall -Fw Calc.py # 不显示命令行窗口,打包成单独一个包
def createDB():
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName("...")
if not db.open():
# 无法连接
query = QSqlQuery()
query.exec('sql语句')
# ...
db.close()
QTableView
+ QSqlTableModel
def initModel(model):
model.setTable('people')
model.setEditStrategy(QSqlTableModel.OnFieldChange)
model.select()
model.setHeaderData(0,Qt.Horizontal,'ID')
model.setHeaderData(1,Qt.Horizontal,'姓名')
model.setHeaderData(2,Qt.Horizontal,'地址')
def createView(title, model):
view = QTableView()
view.setModel(model)
view.setWindowTitle(title)
return view
def findrow(i):
delrow = i.row()
print('del row = %s' % str(delrow))
def addrow():
ret = model.insertRows(model.rowCount(),1)
print('insertRow = %s' % str(ret))
if __name__ == '__main__':
app = QApplication(sys.argv)
db = QSqlDatabase.addDatabase('QSQLITE')
model = QSqlTableModel()
delrow = -1
initModel(model)
view = createView('展示数据',model)
view.clicked.connect(findrow)
dlg = QDialog()
layout = QVBoxLayout()
layout.addWidget(view)
addBtn = QPushButton('添加一行')
addBtn.clicked.connect(addrow)
delBtn = QPushButton('删除一行')
delBtn.clicked.connect(lambda:model.removeRow(view.currentIndex().row()))
layout.addWidget(addBtn)
layout.addWidget(delBtn)
dlg.setLayout(layout)
dlg.resize(600,400)
dlg.show()
sys.exit(app.exec_())
分页显示数据:limit关键字