注:原創不易,轉載請務必注明原作者和出處,感謝支持!
一 寫在開頭
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_())