主要工具
工具 | 详解 |
---|---|
QLabel | 用于显示文本或图像 |
QLineEdit | 允许用户输入一行文本 |
QTextEdit | 允许用户输入多行文本 |
QPushButton | 一个用于调用动作的命令按钮 |
QRadioButton | 可以从多个选项中选择一个。 |
QCheckBox | 可以选择一个以上的选项 |
QSpinBox | 能够增加/减少一个整数值 |
QScrollBar | 可以访问显示孔径以外的小部件的内容。 |
QSlider | 能够线性地改变边界值。 |
QComboBox | 提供一个可供选择的下拉式项目列表 |
QMenuBar | 水平条,用于容纳QMenu对象。 |
QStatusBar | 通常在QMainWindow的底部,提供状态信息。 |
QToolBar | 通常在QMainWindow的顶部或浮动。包含行动按钮 |
QListView | 在ListMode或IconMode中提供一个可选择的项目列表 |
QPixmap | 用于显示在QLabel或QPushButton对象上的非屏幕图像表示。 |
QDialog | 模态或无模式窗口,可以向父窗口返回信息 |
dialog、widget、mainwindow的区别
dialog
这个dialog窗口只是为了给人们提供更好的可视化操作,但是对于程序员而言,这个操作并不是立刻执行的;而是当在窗口选择关闭后,才将选择的结果返回给后台,后台才可以根据选择的结果进行相应的操作。
dialog右上角是问号和关闭,它主要用来和用户进行简单交互,比如你安装一个软件会让你点下一步下一步继续安装的,很多是dialog。
有exec函数,如果是dialog窗口,后边的窗口时不可选的;
有show函数
widget
所有用户界面对象的基类。
右上角是缩小放大和退出,是很多简单应用窗口的顶层窗口。
dialog有show函数,如果通过这个函数显示这两种类型的窗口,则两个窗口都是可选的;
主要是在上面放置布局和控件;
mainwindow
有菜单,工具栏,状态栏、托盘等功能。
基本介绍
widget
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.move(300, 300)
# 上面两句相当于w.setGeometry(xpos, ypos, width, height)
w.setWindowTitle('Simple')
w.setWindowIcon(QIcon('web.png'))
QToolTip.setFont(QFont('SansSerif', 10))
w.setToolTip('This is a <b>QWidget</b> widget')
w.show()
sys.exit(app.exec_())
- app:每个PyQt5应用都必须创建一个应用对象。sys.argv是一组命令行参数的列表。Python可以在shell里运行,这个参数提供对脚本控制的功能。
- QWidge控件是一个用户界面的基本控件,它提供了基本的应用构造器。默认情况下,构造器是没有父级的,没有父级的构造器被称为窗口(window)。
- exec_:最后,我们进入了应用的主循环中,事件处理器这个时候开始工作。主循环从窗口上接收事件,并把事件派发到应用控件里。当调用exit()方法或直接销毁主控件时,主循环就会结束。sys.exit()方法能确保主循环安全退出。外部环境会收到主控件如何结束的信息。exec_()之所以有个下划线,是因为exec是一个Python的关键字。
自带方法:
- resize
- move
- setGeometry
- setWindowTitle
- setWindowIcon
- setToolTip
其他方法:
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
QPushButton
qbtn = QPushButton('Quit', self)
qbtn.clicked.connect(QCoreApplication.instance().quit)
qbtn.resize(qbtn.sizeHint())
qbtn.move(50, 50)
用法 | 详解 |
---|---|
QPushButton() | 创建按钮 |
setText(str) | 设置按钮显示的文字 |
move(xpos,ypos) | 设置按钮距离父组件左上角的距离 |
resize(w,h) | 设置按钮的大小 |
clicked.connect(函数) | 设置按钮的触发事件 |
按钮的触发函数:
如果按钮的触发函数式 def test(): ……
括号中只能写test(即函数的名称),例:button.clicked.connext(test)
但是如果遇到需要传参的情况,如 def test(a,b): ……
则可以使用lambda表达式进行传参:例:button.clicked.connext(lambda: test(a,b))
封装了一下:
class Button(QPushButton):
def __init__(self):
super().__init__()
def add_btn_absolute(self, window, name, location=None, size=None, func=None):
"""
固定式添加:直接指定按钮的位置(在建立的时候带self)
@param window: 在QMainWindow的继承类中调用本方法时,直接将self传入
@param name: 按钮的显示文本
@param location: 按钮在窗口的位置
@param size: 按钮的尺寸
@param func: 按钮的响应函数
@return: 按钮
"""
btn = QPushButton(name, window)
if size:
btn.resize(size[0], size[1])
else:
btn.resize(btn.sizeHint())
if location:
btn.move(location[0], location[1])
if func:
btn.clicked.connect(func)
def get_btn(self, name, func=None):
"""
div式添加(不带self)
@param name: 按钮的显示文本
@param size: 按钮的尺寸
@param func: 按钮的响应函数
@return: 按钮
"""
btn = QPushButton(name)
btn.setStyleSheet('''
QPushButton
{text-align : center;
background-color : white;
font: bold;
border-color: gray;
border-width: 2px;
border-style: outset;
border-radius: 3px;
padding: 15px;
margin: 15px;
font : 14px;}''')
if func:
btn.clicked.connect(func)
return btn
QMessageBox
需要确定型消息可以使用messagebox进行确认,比如关闭前的确认事件。
在widget的重写类中加入以下函数(不用其他语句),就可以在关闭前进行确认。
def closeEvent(self, event):
reply = QMessageBox.question(self,
'Message',
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No) # 默认选中no按钮
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
栏
底部状态栏。
window.statusBar().showMessage('状态栏显示的文本')
菜单栏
菜单栏结构例子:
def create_menu_bar(window):
m = MenuBar(window)
bar = m.create_bar()
a = m.add_menu(bar, "a")
b = m.add_action(bar, "b")
aa = m.add_menu(a, "aa")
ab = m.add_action(a, "ab")
ac = m.add_menu(a, "ac")
命令:
- MenuBar(window)
- create_bar()
- add_menu(父组件,要添加的菜单名称)
- add_action(父组件,要添加的动作名称)
添加动作例子:
# 创建关闭的动作
exitAct = QAction(QIcon('exit.png'), '&Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.setStatusTip('Exit application')
exitAct.triggered.connect(qApp.quit)
# 建立菜单栏
menubar = self.menuBar()
# 将动作加入菜单栏
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(exitAct)
封装
class MenuBar:
def __init__(self, window):
self.window = window
def create_bar(self):
return self.window.menuBar()
def add_action(self, parent, son_name):
son = QAction(son_name, self.window)
parent.addAction(son)
return son
def add_menu(self, parent, son_name):
son = QMenu(son_name, self.window)
parent.addMenu(son)
return son
右键菜单
在window的函数中添加如下代码,可定义右键菜单:
def contextMenuEvent(self, event):
cmenu = QMenu(self)
newAct = cmenu.addAction("New")
opnAct = cmenu.addAction("Open")
quitAct = cmenu.addAction("Quit")
action = cmenu.exec_(self.mapToGlobal(event.pos()))
if action == quitAct:
qApp.quit()
命令:
- QMenu(window)
- addAction(“动作名字”)
布局
封装
class Layout:
@staticmethod
def add_layout_to_widget(widget, layout):
"""
设置窗口布局
@param widget:窗口
@param layout: 布局
"""
widget.setLayout(layout)
@staticmethod
def layout_h():
"""
创建一个水平布局
@return: 水平布局
"""
return QHBoxLayout()
@staticmethod
def layout_v():
"""
创建一个垂直布局
@return: 垂直布局
"""
return QVBoxLayout()
@staticmethod
def add(parent, son):
"""
将子组件添加到父组件中
@param parent: 父组件
@param son: 子组件
"""
if type(son) == QVBoxLayout or type(son) == QHBoxLayout:
parent.addLayout(son)
else:
parent.addWidget(son)
@staticmethod
def set_padding(parent, num):
parent.addStretch(num)
封装后的命令:
- layout_h()
- layout_v()
- add(parent, son)
- set_padding(parent, num)
- add_layout_to_widget(widget, layout) 将当前布局设置为窗口布局
QAction
方法 | 详解 |
---|---|
setIcon(): | 设置图标; |
setText(): | 设置要显示的文字; |
setToolTip(): | 设置提示文字; |
setShortcut(): | 设置快捷键; |
setCheckable(): | 设置成check选择模式; |
setChecked(): | 设置成选中/未选中(菜单前打勾或不打勾)。 |
信号 | 详解 |
---|---|
triggered | 点击时发射信号 |
hovered | 鼠标悬浮上空时发射该信号; |
toggled | 如果状态为选中,则checked为True,该参数会被发送; |
changed | 只要QAction状态发生改变就会发送,如多了个图标,换了文字等。 |
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QMainWindow, QMenuBar, QMenu,
QAction, QPlainTextEdit, QStyle, QFileDialog,
QMessageBox)
class DemoNotepad(QMainWindow):
def __init__(self, parent=None):
super(DemoNotepad, self).__init__(parent)
self.setWindowTitle('实战PyQt5: QAction Demo-记事本')
self.resize(480, 360)
self.path = None
self.initUi()
def initUi(self):
#设置一个文本编辑器作为中心小部件
self.txtEditor = QPlainTextEdit(self)
self.setCentralWidget(self.txtEditor)
self.initMenuBar()
def initMenuBar(self):
menuBar = self.menuBar()
fileMenu = menuBar.addMenu('文件(&F)')
editMenu = menuBar.addMenu('编辑(&E)')
formatMenu = menuBar.addMenu('格式(&O)')
helpMenu = menuBar.addMenu('帮助(&H)')
style = QApplication.style()
# ==== 文件操作部分 ==== #
#新建文件
aFileNew = QAction('新建(&N)', self)
#添加一个图标
aFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon))
#添加快捷键
aFileNew.setShortcut(Qt.CTRL + Qt.Key_N)
aFileNew.triggered.connect(self.onFileNew)
fileMenu.addAction(aFileNew)
#打开文件
aFileOpen = QAction('打开(&O)...', self)
aFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton))
aFileOpen.setShortcut(Qt.CTRL + Qt.Key_O)
aFileOpen.triggered.connect(self.onFileOpen)
fileMenu.addAction(aFileOpen)
#保存
aFileSave = QAction('保存(&S)', self)
aFileSave.setIcon(style.standardIcon(QStyle.SP_DialogSaveButton))
aFileSave.setShortcut(Qt.CTRL + Qt.Key_S)
aFileSave.triggered.connect(self.onFileSave)
fileMenu.addAction(aFileSave)
#另存为
aFileSaveAs = QAction('另存为(&A)...', self)
aFileSaveAs.triggered.connect(self.onFileSaveAs)
fileMenu.addAction(aFileSaveAs)
#间隔线
fileMenu.addSeparator()
#退出菜单
aFileExit = QAction('退出(&X)', self)
aFileExit.triggered.connect(self.close)
fileMenu.addAction(aFileExit)
# ==== 编辑部分 ==== #
#撤销编辑
aEditUndo = QAction('撤销(&U)',self)
aEditUndo.setShortcut(Qt.CTRL + Qt.Key_Z)
aEditUndo.triggered.connect(self.txtEditor.undo)
editMenu.addAction(aEditUndo)
#恢复编辑
aEditRedo = QAction('恢复(&R)', self)
aEditRedo.setShortcut(Qt.CTRL + Qt.Key_Y)
aEditUndo.triggered.connect(self.txtEditor.redo)
editMenu.addAction(aEditRedo)
#间隔线
editMenu.addSeparator()
#剪切操作
aEditCut = QAction('剪切(&T)', self)
aEditCut.setShortcut(Qt.CTRL + Qt.Key_X)
aEditCut.triggered.connect(self.txtEditor.cut)
editMenu.addAction(aEditCut)
#复制操作
aEditCopy = QAction('复制(&C)', self)
aEditCopy.setShortcut(Qt.CTRL + Qt.Key_C)
aEditCopy.triggered.connect(self.txtEditor.copy)
editMenu.addAction(aEditCopy)
#粘贴操作
aEditPaste = QAction('粘贴(&P)', self)
aEditPaste.setShortcut(Qt.CTRL + Qt.Key_V)
aEditPaste.triggered.connect(self.txtEditor.paste)
editMenu.addAction(aEditPaste)
#删除操作
aEditDel = QAction('删除(&L)', self)
aEditDel.setShortcut(Qt.Key_Delete)
aEditDel.triggered.connect(self.onEditDelete)
editMenu.addAction(aEditDel)
#添加分割线
editMenu.addSeparator()
#全选操作
aEditSelectAll = QAction('全选(&A)', self)
aEditSelectAll.setShortcut(Qt.CTRL + Qt.Key_A)
aEditSelectAll.triggered.connect(self.txtEditor.selectAll)
editMenu.addAction(aEditSelectAll)
# ==== 格式设置部分 ==== #
aFmtAutoLine = QAction('自动换行(&W)', self)
aFmtAutoLine.setCheckable(True)
aFmtAutoLine.setChecked(True)
aFmtAutoLine.triggered[bool].connect(self.onFormatAutoLine)
formatMenu.addAction(aFmtAutoLine)
# ==== 帮助部分 ==== #
aHelpAbout = QAction('关于(&A)...', self)
aHelpAbout.triggered.connect(self.onHelpAbout)
helpMenu.addAction(aHelpAbout)
def msgCritical(self, strInfo):
dlg = QMessageBox(self)
dlg.setIcon(QMessageBox.Critical)
dlg.setText(strInfo)
dlg.show()
def onFileNew(self):
self.txtEditor.clear()
def onFileOpen(self):
path,_ = QFileDialog.getOpenFileName(self, '打开文件', '', '文本文件 (*.txt)')
if path:
try:
with open(path, 'rU') as f:
text = f.read()
except Exception as e:
self.msgCritical(str(e))
else:
self.path = path
self.txtEditor.setPlainText(text)
def onFileSave(self):
if self.path is None:
return self.onFileSaveAs()
self._saveToPath(self.path)
def onFileSaveAs(self):
path,_ = QFileDialog.getSaveFileName(self, '保存文件', '', '文本文件 (*.txt)')
if not path:
return
self._saveToPath(path)
def _saveToPath(self, path):
text = self.txtEdit.toPlainText()
try:
with open(path, 'w') as f:
f.write(text)
except Exception as e:
self.msgCritical(str(e))
else:
self.path = path
def onEditDelete(self):
tc = self.txtEditor.textCursor()
#tc.select(QtGui.QTextCursor.BlockUnderCursor) 这样删除一行
tc.removeSelectedText()
def onFormatAutoLine(self, autoLine):
if autoLine:
self.txtEditor.setLineWrapMode(QPlainTextEdit.WidgetWidth)
else:
self.txtEditor.setLineWrapMode(QPlainTextEdit.NoWrap)
def onHelpAbout(self):
QMessageBox.information(self, '实战PyQt5', 'PyQt5实现的文本编辑器演示版')
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DemoNotepad()
window.show()
sys.exit(app.exec())
事件与信号
按键
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
self.close()
鼠标
def mouseMoveEvent(self, e):
x = e.x()
y = e.y()
text = "x: {0}, y: {1}".format(x, y)
self.label.setText(text)
完整代码:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QApplication, QGridLayout, QLabel
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
grid = QGridLayout()
grid.setSpacing(10)
x = 0
y = 0
self.text = "x: {0}, y: {1}".format(x, y)
self.label = QLabel(self.text, self)
grid.addWidget(self.label, 0, 0, Qt.AlignTop)
self.setMouseTracking(True)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 200)
self.setWindowTitle('Event object')
self.show()
def mouseMoveEvent(self, e):
x = e.x()
y = e.y()
text = "x: {0}, y: {1}".format(x, y)
self.label.setText(text)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
按钮
sender返回的值为按钮的文本
def buttonClicked(self):
sender = self.sender()
self.statusBar().showMessage(sender.text() + ' was pressed')
对话框
输入框
self.btn.clicked.connect(self.showDialog)
def showDialog(self):
text, ok = QInputDialog.getText(self,
'Input Dialog',
'Enter your name:')
if ok:
self.le.setText(str(text))
完整代码:
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit,
QInputDialog, QApplication)
import sys
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.btn = QPushButton('Dialog', self)
self.btn.move(20, 20)
self.btn.clicked.connect(self.showDialog)
self.le = QLineEdit(self)
self.le.move(130, 22)
self.setGeometry(300, 300, 290, 150)
self.setWindowTitle('Input dialog')
self.show()
def showDialog(self):
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_())
选择文件
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)
完整代码
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import (QMainWindow, QTextEdit,
QAction, QFileDialog, QApplication)
from PyQt5.QtGui import QIcon
import sys
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_())