PyQt5之窗口类型
注:原创不易,转载请务必注明原作者和出处,感谢支持!
一 写在开头
1.1 本文内容
本文的主要内容:PyQt中的窗口部件:QMainWindow,QWidget,QDialog。
上述三种窗口部件都是用来创建窗口的,可以直接使用,也可以继承后再使用。它们的异同如下:
- QMainWindow窗口可以包含菜单栏、工具栏、状态栏、标题栏等,是最常见的窗口形式,是GUI程序的主窗口。
- QDialog是对话框窗口的基类。对话框主要用来执行短期任务,或者与用户进行互动,它可以是模态的,也可是非模态的。QDialog窗口没有菜单栏、工具栏、状态栏等。
- QWidget即可以用来作为顶层窗口(QMainWindow),可以嵌入到其他窗口中。
三者之间的继承关系如下图:
二 QMainWindow
2.1 知识铺垫
何为顶层窗口?如果一个窗口包含一个或多个窗口,那么这个窗口就是父窗口,被包含的窗口则是子窗口。没有父窗口的窗口则是顶层窗口。QMainWindow就是一个顶层窗口,它可以包含很多界面元素,如菜单栏、工具栏、状态栏、子窗口等等。QMainWindow元素布局如下图(来自Qt文档)。
QMainWindow常用的方法有:
方法 | 描述 |
---|---|
addToolBar() | 添加工具栏 |
centralWidget() | 返回窗口中心的控件,未设置时返回NULL |
menuBar() | 返回主窗口的菜单栏 |
setCentralWidget() | 设置窗口中心的控件 |
setStatusBar() | 设置状态栏 |
statusBar() | 获得状态栏对象后,调用状态栏对象的showMessage()方法显示状态栏信息 |
2.2 QMainWindow实例
什么也不设置的“空白”QMainWindow,代码及效果图如下所示。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QMainWindow()
w.show()
sys.exit(app.exec_())
我们通过一个仿照Windows系统的中记事本程序的小实例来了解QMainWindow的使用。
# text-editor.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import webbrowser
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QMainWindow, QTextEdit, QAction, QApplication, \
QMessageBox, QFileDialog, QDesktopWidget
class TextEditor(QMainWindow):
'''
TextEditor : 一个简单的记事本程序
'''
def __init__(self):
super().__init__()
self.copiedText = ''
self.initUI()
# 初始化窗口界面
def initUI(self):
# 设置中心窗口部件为QTextEdit
self.textEdit = QTextEdit()
self.setCentralWidget(self.textEdit)
self.textEdit.setText('')
# 定义一系列的Action
# 退出
exitAction = QAction(QIcon('./images/exit.png'), 'Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.setStatusTip('Exit application')
exitAction.triggered.connect(self.close)
# 新建
newAction = QAction(QIcon('./images/new.png'), 'New', self)
newAction.setShortcut('Ctrl+N')
newAction.setStatusTip('New application')
newAction.triggered.connect(self.__init__)
# 打开
openAction = QAction(QIcon('./images/open.png'), 'Open', self)
openAction.setShortcut('Ctrl+O')
openAction.setStatusTip('Open Application')
openAction.triggered.connect(self.open)
# 保存
saveAction = QAction(QIcon('./images/save.png'), 'Save', self)
saveAction.setShortcut('Ctrl+S')
saveAction.setStatusTip('Save Application')
saveAction.triggered.connect(self.save)
# 撤销
undoAction = QAction(QIcon('./images/undo.png'), 'Undo', self)
undoAction.setShortcut('Ctrl+Z')
undoAction.setStatusTip('Undo')
undoAction.triggered.connect(self.textEdit.undo)
# 重做
redoAction = QAction(QIcon('./images/redo.png'), 'Redo', self)
redoAction.setShortcut('Ctrl+Y')
redoAction.setStatusTip('Redo')
redoAction.triggered.connect(self.textEdit.redo)
# 拷贝
copyAction = QAction(QIcon('./images/copy.png'), 'Copy', self)
copyAction.setShortcut('Ctrl+C')
copyAction.setStatusTip('Copy')
copyAction.triggered.connect(self.copy)
# 粘贴
pasteAction = QAction(QIcon('./images/paste.png'), 'Paste', self)
pasteAction.setShortcut('Ctrl+V')
pasteAction.setStatusTip('Paste')
pasteAction.triggered.connect(self.paste)
# 剪切
cutAction = QAction(QIcon('./images/cut.png'), 'Cut', self)
cutAction.setShortcut('Ctrl+X')
cutAction.setStatusTip('Cut')
cutAction.triggered.connect(self.cut)
# 关于
aboutAction = QAction(QIcon('./images/about.png'), 'About', self)
aboutAction.setStatusTip('About')
aboutAction.triggered.connect(self.about)
# 添加菜单
# 对于菜单栏,注意menuBar,menu和action三者之间的关系
# 首先取得QMainWindow自带的menuBar:menubar = self.menuBar()
# 然后在menuBar里添加Menu:fileMenu = menubar.addMenu('&File')
# 最后在Menu里添加Action:fileMenu.addAction(newAction)
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction(newAction)
fileMenu.addAction(openAction)
fileMenu.addAction(saveAction)
fileMenu.addAction(exitAction)
editMenu = menubar.addMenu('&Edit')
editMenu.addAction(undoAction)
editMenu.addAction(redoAction)
editMenu.addAction(cutAction)
editMenu.addAction(copyAction)
editMenu.addAction(pasteAction)
helpMenu = menubar.addMenu('&Help')
helpMenu.addAction(aboutAction)
# 添加工具栏
# 对于工具栏,同样注意ToolBar和Action之间的关系
# 首先在QMainWindow中添加ToolBar:tb1 = self.addToolBar('File')
# 然后在ToolBar中添加Action:tb1.addAction(newAction)
tb1 = self.addToolBar('File')
tb1.addAction(newAction)
tb1.addAction(openAction)
tb1.addAction(saveAction)
tb2 = self.addToolBar('Edit')
tb2.addAction(undoAction)
tb2.addAction(redoAction)
tb2.addAction(cutAction)
tb2.addAction(copyAction)
tb2.addAction(pasteAction)
tb3 = self.addToolBar('Exit')
tb3.addAction(exitAction)
# 添加状态栏,以显示每个Action的StatusTip信息
self.statusBar()
self.setGeometry(0, 0, 600, 600)
self.setWindowTitle('Text Editor')
self.setWindowIcon(QIcon('./images/text.png'))
self.center()
self.show()
# 主窗口居中显示
def center(self):
screen = QDesktopWidget().screenGeometry()
size = self.geometry()
self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)
# 定义Action对应的触发事件,在触发事件中调用self.statusBar()显示提示信息
# 重写closeEvent
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Confirm', \
'Are you sure to quit without saving ?', \
QMessageBox.Yes | QMessageBox.No, \
QMessageBox.No)
if reply == QMessageBox.Yes:
self.statusBar().showMessage('Quiting...')
event.accept()
else:
event.ignore()
self.save()
event.accept()
# open
def open(self):
self.statusBar().showMessage('Open Text Files')
fname = QFileDialog.getOpenFileName(self, 'Open file', '/home')
self.statusBar().showMessage('Open File')
if fname[0]:
f = open(fname[0], 'r')
with f:
data = f.read()
self.textEdit.setText(data)
# save
def save(self):
self.statusBar().showMessage('Add extension to file name')
fname = QFileDialog.getSaveFileName(self, 'Save File')
if (fname[0]):
data = self.textEdit.toPlainText()
f = open(fname[0], 'w')
f.write(data)
f.close()
# copy
def copy(self):
cursor = self.textEdit.textCursor()
textSelected = cursor.selectedText()
self.copiedText = textSelected
# paste
def paste(self):
self.textEdit.append(self.copiedText)
# cut
def cut(self):
cursor = self.textEdit.textCursor()
textSelected = cursor.selectedText()
self.copiedText = textSelected
self.textEdit.cut()
# about
def about(self):
url = 'https://en.wikipedia.org/wiki/Text_editor'
self.statusBar().showMessage('Loading url...')
webbrowser.open(url)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = TextEditor()
sys.exit(app.exec_())
三 QWidget
QWidget类是所有用户界面对象的基类,所有的窗口和控件都直接或间接继承自QWidget类。QWidget类相关的方法。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QToolTip
from PyQt5.QtGui import QIcon, QFont
if __name__ == '__main__':
app = QApplication(sys.argv)
w = QWidget()
btn = QPushButton(w)
btn.setText('Button')
btn.move(20, 20)
w.resize(300, 200)
w.move(250, 200)
w.setWindowTitle('QWidget')
# setWindowIcon()用于设置应用程序图标
w.setWindowIcon(QIcon('./icon.png'))
# setFont()为QToolTip设定字体
QToolTip.setFont(QFont('Monospace Regular', 20))
w.setToolTip('这是一个<b>气泡提示!</b>')
w.show()
print('QWidget:')
print('w.x() = %d' % w.x())
print('w.y() = %d' % w.y())
print('w.width() = %d' % w.width())
print('w.height() = %d' % w.height())
print('QWidget.geometry')
print('w.geometry().x() = %d' % w.geometry().x())
print('w.geometry().y() = %d' % w.geometry().y())
print('w.geometry().width() = %d' % w.geometry().width())
print('w.geometry().height() = %d' % w.geometry().height())
sys.exit(app.exec_())
脚本输出为:
QWidget:
w.x() = 250
w.y() = 200
w.width() = 300
w.height() = 200
QWidget.geometry
w.geometry().x() = 250
w.geometry().y() = 200
w.geometry().width() = 300
w.geometry().height() = 200
四 QDialog
QDialog的各种子类提供了各种标准对话框,比如QMessageBox, QFileDialog, QInputDialog, QFontDialog等等。它们之间的继承关系如下图所示。
4.1 QDialog
QDialog类中常用方法:
方法 | 描述 |
---|---|
setWindowTitle() | 设置对话框标题 |
setWindowModality() | 设置窗口模态。取值如下: Qt.NonModal - 非模态 Qt.WindowModal - 窗口模态 Qt.ApplicationModal - 应用程序模态 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QDialog
from PyQt5.QtCore import Qt
class DialogWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle('Dialog')
self.resize(350, 300)
self.btn = QPushButton(self)
self.btn.setText('弹出对话框')
self.btn.move(50, 50)
self.btn.clicked.connect(self.showDialog)
self.show()
def showDialog(self):
dialog = QDialog()
btn = QPushButton('ok', dialog)
btn.move(50, 50)
dialog.setWindowTitle('Dialog')
dialog.setWindowModality(Qt.ApplicationModal)
dialog.exec_()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = DialogWindow()
sys.exit(app.exec_())
4.2 QMessageBox
QMessageBox是一种通用的弹出式对话框,用于显示消息,允许用户通过单击不同的标准按钮对消息进行反馈。每个标准按钮都有一个预定义的文本、角色和十六进制数。QMessageBox类提供了许多常用的弹出式对话框,比如提示、警告、错误、询问、关于等对话框。这些不同类型的QMessageBox对话框只是显示时得图标不同,其他功能是一样的。QMessageBox类中常用的方法有:
方法 | 描述 |
---|---|
information(QWidget parent, title, text, buttons, defaultButton) | parent:父窗口 title:对话框标题 text:对话框文本 buttons:多个标准按钮 defaultButton:默认选中的标准按钮 |
question(QWidget parent, title, text, buttons, defaultButton) | 问答对话框 |
warning(QWidget parent, title, text, buttons, defaultButton) | 警告对话框 |
critical(QWidget parent, title, text, buttons, defaultButton) | 严重错误对话框 |
about(QWidget parent, title, text) | 关于对话框 |
setTitle() | 设置标题 |
setText() | 设置消息正文 |
setIcon() | 设置对话框的图片 |
QMessageBox中的标准按钮类型有:
类型 | 描述 |
---|---|
QMessageBox.Ok | 确定 |
QMessageBox.Cancel | 取消 |
QMessageBox.Yes | 是 |
QMessageBox.No | 否 |
QMessageBox.Abort | 终止 |
QMessageBox.Retry | 重试 |
QMessageBox.Ignore | 忽略 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication, QMessageBox, QWidget, QVBoxLayout, \
QPushButton
class MessageBoxWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
vbox = QVBoxLayout()
btn = QPushButton('点击弹出消息框')
btn.clicked.connect(self.showMessageBox)
vbox.addWidget(btn)
self.setLayout(vbox)
self.setWindowTitle('QMessageBox')
self.resize(300, 200)
self.show()
def showMessageBox(self):
QMessageBox.question(self, '标题', '正文内容', QMessageBox.Yes | \
QMessageBox.No, QMessageBox.Yes)
QMessageBox.warning(self, '标题', '正文内容', QMessageBox.Yes | \
QMessageBox.No, QMessageBox.Yes)
QMessageBox.critical(self, '标题', '正文内容', QMessageBox.Yes | \
QMessageBox.No, QMessageBox.Yes)
QMessageBox.about(self, '标题', '正文内容')
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MessageBoxWindow()
sys.exit(app.exec_())
4.3 QInputDialog
QInputDialog控件是一个标准对话框,由一个文本框和两个按钮(OK和Cancel)组成。当用户单击OK按钮后,在父窗口可以接受通过QInputDialog控件输入的信息。在QInputDialog控件中可以输入数字、字符串或者列表中的选择。标签用于提示必要的信息。QInputDialog类常用的方法有:
方法 | 描述 |
---|---|
getInt() | 从控件中获取标准整型输入 |
getDouble() | 从控件中获取标准浮点数输入 |
getText() | 从控件中获取标准字符串输入 |
getItem() | 从控件中获取列表里的选项输入 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget, QFormLayout, QPushButton, QLineEdit, \
QInputDialog, QApplication
class InputDialogWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QFormLayout()
self.btn1 = QPushButton('获得列表里的选项')
self.btn1.clicked.connect(self.getItem)
self.le1 = QLineEdit()
layout.addRow(self.btn1, self.le1)
self.btn2 = QPushButton('获得字符串')
self.btn2.clicked.connect(self.getText)
self.le2 = QLineEdit()
layout.addRow(self.btn2, self.le2)
self.btn3 = QPushButton('获得整数')
self.btn3.clicked.connect(self.getInt)
self.le3 = QLineEdit()
layout.addRow(self.btn3, self.le3)
self.setLayout(layout)
self.setWindowTitle('QInputDialog')
self.show()
def getItem(self):
items = ('C', 'C++', 'Java', 'Python')
item, ok = QInputDialog.getItem(self, 'Select Input Dialog', \
'语言列表', items, 0, False)
if ok and item:
self.le1.setText(item)
def getText(self):
text, ok = QInputDialog.getText(self, 'Text Input Dialog', \
'输入姓名:')
if ok:
self.le2.setText(str(text))
def getInt(self):
num, ok = QInputDialog.getInt(self, 'Integer Input Dialog', \
'输入数字:')
if ok:
self.le3.setText(str(num))
if __name__ == '__main__':
app = QApplication(sys.argv)
w = InputDialogWindow()
sys.exit(app.exec_())
4.4 QFontDialg
QFontDialog控件是一个常用的字体选择对话框,可以让用户选择显示文本的字体样式、字号大小和格式。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QFontDialog, QApplication, \
QPushButton, QLabel
class FontDialogWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
self.btn = QPushButton('选择字体')
self.btn.clicked.connect(self.chooseFont)
self.lb = QLabel('Hello, 测试字体例子')
layout.addWidget(self.btn)
layout.addWidget(self.lb)
self.setLayout(layout)
self.setWindowTitle('FontDialog')
self.show()
def chooseFont(self):
font, ok = QFontDialog.getFont()
if ok:
self.lb.setFont(font)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = FontDialogWindow()
sys.exit(app.exec_())
4.5 QFileDialog
QFileDialog是用于打开和保存文件的标准对话框。QFileDialog在打开文件时使用了文件过滤器,用于显示指定扩展名的文件。也可以设置使用QFileDialog打开文件时的起始目录和指定扩展名的文件。QFileDialog类的常用方法有:
方法 | 描述 |
---|---|
getOpenFileName() | 返回用户所选择文件的名称,并打开该文件 |
getSaveFileName() | 使用用户选择的文件名并保存文件 |
setFileMode() | 可以选择的文件类型,可选枚举常量有: QFileDialog.AnyFile:任何文件 QFileDialog.ExistingFile:已存在的文件 QFileDialog.Directory:文件目录 QFileDialog.ExistingFiles:已存在的多个文件 |
setFilter() | 设置过滤器,只显示过滤器允许的文件类型 |
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QVBoxLayout, QPushButton, \
QLabel, QTextEdit, QFileDialog
from PyQt5.QtCore import QDir
from PyQt5.QtGui import QPixmap
class FileDialogWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
self.btn1 = QPushButton('加载图片')
self.btn1.clicked.connect(self.chooseImage)
self.lb = QLabel()
layout.addWidget(self.btn1)
layout.addWidget(self.lb)
self.btn2 = QPushButton('加载文本文件')
self.btn2.clicked.connect(self.chooseTextFile)
self.content = QTextEdit()
layout.addWidget(self.btn2)
layout.addWidget(self.content)
self.setLayout(layout)
self.setWindowTitle('FileDialg')
self.show()
def chooseImage(self):
fname, _ = QFileDialog.getOpenFileName(self, 'Open file', '/home', \
"Image files (*.jpg *.png *.gif)")
self.lb.setPixmap(QPixmap(fname))
def chooseTextFile(self):
dlg = QFileDialog()
dlg.setFileMode(QFileDialog.AnyFile)
dlg.setFilter(QDir.Files)
if dlg.exec_():
fname = dlg.selectedFiles()
f = open(fname[0], 'r')
with f:
data = f.read()
self.content.setText(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = FileDialogWindow()
sys.exit(app.exec_())