Python3 PyQt5 窗口(国际化/QMainWindow/QWidget/QDialog)
本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/Rozol/article/details/87904498
PyQt5基本介绍见: PyQt5 Qt Designer (Qt设计师)
简介
窗口主要分为: QMainWindow(主窗口) / QWidget(基本窗口) / QDialog(对话框) 这三种.
- QWidget: 最基本的窗口
- QMainWindow: 在 QWidget 的基础上多了 菜单栏 / 工具栏 / 状态栏 / 标题栏 等
- QDialog: 对话框窗口
使用的话:
1.如果是主窗口, 需要菜单栏之类的, 就用QMainWindow.
2.如果是对话框, 就用QDialog.
3.其他不确定的窗口, 都可以用QWidget.
文本国际化
这里先讲下文本国际化, 如果不进行国际化, 那么你运行之后, 发现控件都是英文的.
自带的国际化
PyQt5自带的翻译文件在C:\Code\Python_Vir\python1\Lib\site-packages\PyQt5\Qt\\translations\
目录下, 不过翻译并不完全, 由于已经被编译成.qm
文件, 你也改不了它.
from PyQt5.Qt import QTranslator, QLocale, QLibraryInfotranslator = QTranslator(app)
if translator.load("qt_" + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath)):app.installTranslator(translator)
效果:
自定义的国际化
编写文本时, 有原来的"content"
变成现在的self.tr("content")
, 其他除了要先加载翻译者之外, 均不变.
def initUI(self):# 加载翻译文件translator = QTranslator(app)if translator.load('qt_zh_CN'):app.installTranslator(translator)btn = QPushButton(self.tr("Color"), self)btn.clicked.connect(self.click)self.setGeometry(300, 300, 550, 350)self.setWindowTitle('Dialog')self.show()btn = QPushButton(self.tr("Color"), self)
btn.clicked.connect(self.click)def click(self):QColorDialog.getColor()
1.生成.ts文件
# 创建.ts文件, 存在则更新
pylupdate5 international.py -ts qt_zh_CN.ts
2.打开Linguist进行手动翻译, 翻译完点保存
linguist qt_zh_CN.ts
3.生成.qm文件
lrelease qt_zh_CN.ts
效果:
自己写的是实现了国际化, 原生的英文界面你也拿它没辙.
另外翻译出现问题可以通过修改.ts来修正.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_CN">
<context><name>Widget</name><message><location filename="international.py" line="24"/><source>Color</source><translatorcomment>颜色</translatorcomment><translation>颜色</translation></message>
</context>
</TS>
比如代码位置变了, 会给你打个过时的标签, 如
<translation type="obsolete">取消</translation>
这时只需要把type="obsolete"
删掉, 然后把line="24"
改成正确的行就行了.
另外, 其实翻译只需要<translation>颜色</translation>
标签就够了, <translatorcomment>颜色</translatorcomment>
可有可无.
自带的国际化 + 自定义的国际化
只需要同时加载两个翻译者就好了.
def initUI(self):# 加载翻译文件translator_sys = QTranslator(app)translator_my = QTranslator(app)if translator_sys.load("qt_" + QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath)) and translator_my.load('qt_zh_CN'):app.installTranslator(translator_sys)app.installTranslator(translator_my)btn = QPushButton(self.tr("Color"), self)btn.clicked.connect(self.click)self.setGeometry(300, 300, 550, 350)self.setWindowTitle('Dialog')self.show()def click(self):QColorDialog.getColor()
效果:
如果需要切换语言
app.removeTranslator(translator_my)
app.installTranslator(translator_sys)
注: 为了敲代码效率, 我的所有案例代码都不会用self.tr("xxx")
这种方式来写, 而是直接写"xxx"
, 包括这篇文章的以下代码.
QMainWindow 主窗口
QMainWindow 是一个顶层窗口(没有父窗口), 包含了 MenuBar(菜单栏) / ToolBars(工具栏) / Dock Widgets(停靠控件区) / Status Bar(状态栏), 中心窗口区被一个 QWidget 占着, 可通过setCentralWidget()
来设置(不能通过 setLayout() 设置).
创建主窗口
我们有两种方式创建, 第一种是创建对象, 第二种是继承类, 一般我们会采用第二种, 包括博客文章的案例代码采用这种方式.
第一种: 创建对象方式
#!/usr/bin/env python
# coding=utf-8
__author__ = 'Luzhuo'
__date__ = '2019/2/19'
# QMainWindow 创建主窗口import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtGui import QIcondef mainwindow1():'''方式一: 通过创建对象方式创建主窗口'''# 每个PyQt5应用都要创建一个应用对象 (sys.argv是一组命令行参数列表)app = QApplication(sys.argv)# 主窗口w = QMainWindow()w.setGeometry(300, 300, 550, 350)w.setWindowTitle('我是窗口标题')w.setWindowIcon(QIcon('icon.png')) # 设置图标w.show() # 展示该控件# 退出主程序sys.exit(app.exec_())if __name__ == '__main__':mainwindow1()
第二种: 继承类方式
class MainWindow(QMainWindow):'''方式二: 通过继承类方式创建主窗口'''def __init__(self):super().__init__()self.initUI()def initUI(self):self.setGeometry(300, 300, 550, 350)self.setWindowTitle('我是窗口标题')self.setWindowIcon(QIcon('icon.png')) # 设置图标self.show() # 展示该控件if __name__ == '__main__':app = QApplication(sys.argv)ex = MainWindow()sys.exit(app.exec_())
效果:
窗口位置与大小
我们有三种方式设置窗口的位置与大小:
第一种:
self.resize(550, 350) # 改变窗口大小(px)
self.move(300, 300) # 移动控件位置(px)
第二种 (是第一种两方法的结合):
self.setGeometry(300, 300, 550, 350) # 放在屏幕上并设置大小
第三种 (居中显示):
qr = self.frameGeometry() # 得到应用主窗体大小
cp = QDesktopWidget().availableGeometry().center() # QDesktopWidget 得到桌面大小, availableGeometry 得到屏幕分辨率, center 得到中间点坐标
qr.moveCenter(cp) # 将 应用中间点 移到 屏幕中间点
self.resize(550, 350)
self.move(qr.topLeft()) # 将 应用左上标 设为 矩形左上标
窗口的几何结构:
窗口分为 不含外边框的几何结构 和 含外边框的几何结构.
# --- 无边框几何结构 ---
# 修改客户区的大小(鼠标可修改)
self.resize(550, 350)
self.resize(QSize(550, 350))
self.setGeometry(300, 300, 550, 350)
self.setGeometry(QRect(300, 300, 550, 350))# 修改客户区的大小(鼠标不可修改)
self.setFixedWidth(550) # 宽度固定 (高度可变)
self.setFixedHeight(350) # 高度固定
self.setFixedSize(550, 350) # 宽度与高度都固定
self.setFixedSize(QSize(550, 350))# 获取客户区大小
qsize = self.size()
qrect = self.geometry()
width = self.width()
height = self.height()print("QSize - width:{w} height:{h}".format(w=qsize.width(), h=qsize.height()))
print("QRect - x:{x} y:{y} width:{w} height:{h}".format(x=qrect.x(), y=qrect.y(), w=qrect.width(), h=qrect.height()))
print("width:{w} height:{h}".format(w=width, h=height))# --- 有边框几何结构 ---
# 设置窗口位置
self.move(300, 300)
self.move(QPoint(300, 300))# 获取窗口位置与大小
qrect = self.frameGeometry() # 位置和大小
qpoint = self.pos() # 左上角坐标print("QRect - x:{x} y:{y} width:{w} height:{h}".format(x=qrect.x(), y=qrect.y(), w=qrect.width(), h=qrect.height()))
print("QRect - x:{x} y:{y}".format(x=qpoint.x(), y=qpoint.y()))
关闭窗口
关闭窗口, 除了点击右上角的红×退出外, 还有自己写退出的逻辑(与退出槽连接).
退出应用的信号槽连接:
qbtn.clicked.connect(QCoreApplication.instance().quit)
完整代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
from PyQt5.QtCore import QCoreApplicationclass MainWindow(QMainWindow):'''退出窗口'''def __init__(self):super().__init__()self.initUI()def initUI(self):qbtn = QPushButton('Quit', self)qbtn.resize(qbtn.sizeHint())qbtn.move(50, 50)# 按钮的点击信号 与 应用退出槽 进行连接qbtn.clicked.connect(QCoreApplication.instance().quit)self.setGeometry(300, 300, 550, 350)self.setWindowTitle('窗口的关闭')self.show()if __name__ == '__main__':app = QApplication(sys.argv)ex = MainWindow()sys.exit(app.exec_())
应用退出事件:
如果你想在应用退出时做点操作, 比如弹出个对话框之类的. 那么就需要重写def closeEvent(self, event):
方法
def closeEvent(self, event):if True:event.accept() # 执行事件else:event.ignore() # 忽略事件
MenuBar 菜单栏
菜单栏由 菜单栏 / 菜单 / 子菜单 / 动作 组成.
创建动作:
jumpAct = QAction(QIcon('icon.png'), '&Jump to Source', self) # 图标 / exit标签
jumpAct.setShortcut('F4') # 快捷键
jumpAct.setStatusTip('这是 Jump to Source 的提示信息') # 状态栏提示信息
jumpAct.triggered.connect(qApp.quit) # 触发 quit 事件
创建子菜单:
impMenu = QMenu('Tool Windows', self)
impMenu.addAction(strucAct) # 往子菜单添加一个动作
创建菜单:
viewMenu = menubar.addMenu('&View') # 添加View菜单
viewMenu.addMenu(impMenu) # 添加子菜单
viewMenu.addSeparator() # 添加分隔线
viewMenu.addAction(jumpAct) # 添加 动作
创建菜单栏:
menubar = self.menuBar()
完整代码:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, qApp, QMenu
from PyQt5.QtGui import QIconclass MainWindow(QMainWindow):'''菜单栏'''def __init__(self):super().__init__()self.initUI()def initUI(self):# 创建动作jumpAct = QAction(QIcon('icon.png'), '&Jump to Source', self) # 图标 / exit标签jumpAct.setShortcut('F4') # 快捷键jumpAct.setStatusTip('这是 Jump to Source 的提示信息') # 状态栏提示信息jumpAct.triggered.connect(qApp.quit) # 触发 quit 事件strucAct = QAction(QIcon('icon.png'), '&Structure', self)strucAct.setShortcut("Alt+7")# 创建子菜单impMenu = QMenu('Tool Windows', self)impMenu.addAction(strucAct) # 往子菜单添加一个动作# 创建菜单栏menubar = self.menuBar()# 创建菜单viewMenu = menubar.addMenu('&View') # 添加View菜单viewMenu.addMenu(impMenu) # 添加子菜单viewMenu.addSeparator() # 添加分隔线viewMenu.addAction(jumpAct) # 添加 动作self.setGeometry(300, 300, 550, 350)self.setWindowTitle('菜单栏')self.show()if __name__ == '__main__':app = QApplication(sys.argv)ex = MainWindow()sys.exit(app.exec_())
效果:
勾选动作:
当然, 菜单里的动作除了上述比较常见的外, 还有一种动作, 叫勾选动作
statusbarAct = QAction('View statusbar', self, checkable=True)
statusbarAct.setStatusTip('View statusbar')
statusbarAct.setChecked(True) # True为默认选中状态
statusbarAct.triggered.connect(self.toggleMenu)
效果:
右键上下菜单:
菜单除了出现在菜单栏, 它还有一种形式, 就是我们采用的右键菜单.
有实现右键菜单, 就要重写 上下文菜单事件 (def contextMenuEvent(self, event):
)
1.创建子菜单, 并添加动作
cmenu = QMenu(self)
quitAct = cmenu.addAction("Quit")
2.获取用户选择的动作, 然后就动作进行判断是哪个动作
action = cmenu.exec_(self.mapToGlobal(event.pos())) # mapToGlobal 将组件相对坐标转为窗口绝对坐标, exec_ 显示菜单# 如果触发的动作为 quitAct 则退出应用
if action == quitAct:qApp.quit()
完整代码
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())) # mapToGlobal 将组件相对坐标转为窗口绝对坐标, exec_ 显示菜单# 如果触发的动作为 quitAct 则退出应用if action == quitAct:qApp.quit()
效果:
ToolBars 工具栏
工具栏主要是为了方便用户使用而存在的, 其功能都应该能够在菜单栏找到(大学设计书上是这么说的, 不过菜单栏没这功能又能把我咋地 _)
创建一个动作
# 创建一个动作
exitAct = QAction(QIcon('icon.png'), 'Exit', self)
exitAct.setShortcut('Ctrl+Q')
exitAct.triggered.connect(qApp.quit)
把这个动作添加到工具栏
# 将(Exit)动作添加到工具栏
self.toolbar = self.addToolBar('Exit') # 添加Exit工具栏
self.toolbar.addAction(exitAct)
效果
添加控件:
ToolBar除了能添加动作外, 还能添加任意控件.
lineEdit = QLineEdit()
self.toolbar.addSeparator() # 分隔线
self.toolbar.addWidget(lineEdit)
Dock Widgets 停靠控件区
停靠空间区的布局嵌套是有QDockWidget来完成的.
需要创建一个Widget设置到DockWidget上.
1.创建Widget
w = QWidget()
2.创建DockWidget
dock = QDockWidget("窗口1")
dock.setWidget(w) # 为 dockwidget 设置 widget
dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable) # 设置特征(DockWidgetFeature)
self.addDockWidget(Qt.LeftDockWidgetArea, dock) # 添加 dockwidget 到主窗口
效果:
嵌套布局:
当你添加多个 DockWidget 到主窗口时, 你会发现这些窗口只能往左右放, 若要实现往两 DockWidget 中间放, 则要开启嵌套.
self.setDockNestingEnabled(True)
排列组合:
若要将这些小窗口按水平(或垂直 或水平+垂直)排列, 将其水平分隔成同样的宽度.
self.splitDockWidget(win1Dock, win2Dock, Qt.Horizontal)
self.splitDockWidget(win2Dock, win3Dock, Qt.Horizontal)
self.splitDockWidget(win1Dock, win4Dock, Qt.Vertical)
tab页:
多个窗口合成一个窗口, 窗口通过tab标签切换.
self.tabifyDockWidget(win1Dock, win2Dock)
综合代码
def getDock(self, name):w = QWidget()dock = QDockWidget(name)dock.setWidget(w)dock.setFeatures(dock.DockWidgetFloatable | dock.DockWidgetMovable)return dockdef initUI(self):win1Dock = self.getDock("win1")win2Dock = self.getDock("win2")win3Dock = self.getDock("win3")win4Dock = self.getDock("win4")self.addDockWidget(Qt.LeftDockWidgetArea, win1Dock)self.addDockWidget(Qt.RightDockWidgetArea, win2Dock)self.addDockWidget(Qt.BottomDockWidgetArea, win3Dock)self.addDockWidget(Qt.BottomDockWidgetArea, win4Dock)# 实现任意嵌套self.setDockNestingEnabled(True)# 实现分隔 (可水平 / 垂直 / 垂直+水平 从而实现任意效果)self.splitDockWidget(win1Dock, win2Dock, Qt.Horizontal)self.splitDockWidget(win2Dock, win3Dock, Qt.Horizontal)self.splitDockWidget(win1Dock, win4Dock, Qt.Vertical)# tab页self.tabifyDockWidget(win1Dock, win2Dock)
综合效果
QtWidgets.QDockWidget.DockWidgetFeature
Constant | Description |
---|---|
QDockWidget.DockWidgetClosable | 可关闭 |
QDockWidget.DockWidgetMovable | 可移动 |
QDockWidget.DockWidgetFloatable | 可漂浮 |
QDockWidget.DockWidgetVerticalTitleBar | 垂直标题栏 |
QDockWidget.AllDockWidgetFeatures | 所有特性 |
QDockWidget.NoDockWidgetFeatures | 没有特性 |
QtCore.Qt.DockWidgetArea
Constant | Description |
---|---|
Qt.LeftDockWidgetArea | 主窗口左侧 |
Qt.RightDockWidgetArea | 主窗口右侧 |
Qt.TopDockWidgetArea | 主窗口顶部 |
Qt.BottomDockWidgetArea | 主窗口底部 |
Qt.AllDockWidgetAreas | 任意位置 |
Qt.NoDockWidgetArea | 不知道 |
Status Bar 状态栏
状态栏一般显示一些提示信息, 可以是临时的, 也可以是永久的.
一般在 状态栏的左侧 显示 实时信息, 而在其右侧显示 永久信息.
显示实时信息:
self.statusbar = self.statusBar()self.statusbar.showMessage('这是状态栏信息')
self.statusbar.showMessage('这是状态栏信息', 5 * 1000) # 提示5s消失
添加控件:
label1 = QLabel("This is a Label")
label2 = QLabel("CopyRight @ Luzhuo.me 2019")# addWidget 在左侧添加临时控件
self.statusbar.addWidget(label1)
# addWidget 在右侧添加永久控件
self.statusbar.addPermanentWidget(label2)
效果:
QWidget 基本窗口
QWidget 是所有用户界面对象的基类. 准确的说它并不属于窗口类型, 而是属于控件类型.
注: 只有Slots的表格, 注释与实际执行一致; 有Func & Slots的表格, ()内为Slots的执行效果, 没有()的为两者实际执行一致
窗口:
方法:
bool_isW = self.isWindow() # 是否为独立窗口
widget = self.window() # 当前部件所在的独立窗口
widget_parent = self.parentWidget() # 得到父窗口
窗口样式:
默认窗口样式 是使用 当前操作系统的原生窗口样式, 在不同的操作系统下原生窗口样式显示的效果不一样.
我们可以定制窗口样式.
窗口类型 (|组合):
Qt.WindowType 枚举 | Description |
---|---|
Qt.Widget | 默认(最小化 + 最大化 + 关闭) |
Qt.Window | 普通窗口(最小化 + 最大化 + 关闭) |
Qt.Dialog | 对话框(?号 + 关闭) |
Qt.Sheet | macOS 表单 |
Qt.Drawer | macOS 抽屉。 |
Qt.Popup | 泡泡(无边框) |
Qt.Tool | 工具窗口 |
Qt.ToolTip | 提示泡泡 (无边框 + 无任务栏) |
Qt.SplashScreen | 启动屏(无边框 + 无任务栏) |
Qt.Desktop | 桌面 |
Qt.SubWindow | 子窗口(无按钮 + 有标题) |
Qt.ForeignWindow | 窗口句柄 |
Qt.CoverWindow | 封面窗口 (最小化时显示) |
窗口外观标志:
Qt.WindowFlags 枚举 | Description |
---|---|
Qt.MSWindowsFixedSizeDialogHint | 窗口无法调整大小 |
Qt.MSWindowsOwnDC | 添加上下文菜单 |
Qt.BypassWindowManagerHint | 禁用所有窗口管理器 |
Qt.X11BypassWindowManagerHint | 忽视窗口管理器 |
Qt.FramelessWindowHint | 没有边框的窗口 |
Qt.NoDropShadowWindowHint | 禁用窗口阴影 |
自定义窗口:
Qt.WindowFlags 枚举 | Description |
---|---|
Qt.CustomizeWindowHint | 自定义窗口标题栏 (以下标志与此标志一起用, 否则有默认标题栏) |
Qt.WindowTitleHint | 显示窗口标题栏 |
Qt.WindowSystemMenuHint | 显示系统菜单 |
Qt.WindowMinimizeButtonHint | 显示最小化按钮 |
Qt.WindowMaximizeButtonHint | 显示最大化按钮 |
Qt.WindowMinMaxbuttonHint | 显示最小化按钮和最大化按钮 |
Qt.WindowCloseButtonHint | 显示关闭按钮 |
Qt.WindowContextHelpButtonHint | 显示?按钮 |
Qt.MacWindowToolBarButtonHint | 在macOS添加工具栏按钮 |
Qt.WindowFullscreenButtonHint | 在macOS添加全屏按钮 |
Qt.BypassGraphicsProxyWidget | 阻止窗口自动嵌入场景 |
Qt.WindowShadeButtonHint | 用阴影按钮来代替最小化按钮 |
Qt.WindowStaysOnTopHint | 总是最顶层的窗口 |
Qt.WindowStaysOnBottomHint | 总是最底层的窗口 |
其他高大上的标志:
Qt.WindowFlags 枚举 | Description |
---|---|
Qt.WindowTransparentForInput | 窗口仅输入, 不接受输入 |
Qt.WindowOverridesSystemGestures | 窗口有自己的手势, 并禁用系统级手势 |
Qt.WindowDoesNotAcceptFocus | 不接受输入焦点 |
Qt.MaximizeUsingFullscreenGeometryHint | 最大化窗口时, 尽可能多的占用屏幕 (取决于平台是否支持, 且可能放置在系统UI下) |
Qt.WindowType_Mask | 提取窗口类型掩码 |
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.MSWindowsFixedSizeDialogHint | Qt.Dialog)
窗口风格:
方法:
list_style = QStyleFactory.keys() # 当前平台支持的 QStyle 窗口风格样式
self.setStyle(QStyleFactory.create(list_style[1])) # 设置窗口风格
QApplication.setStyle(QStyleFactory.create("WindowsXP")) # 给 App 设置窗口风格, 其他Widget默认(无设置)使用App的风格
窗口最大化:
Slots & Func | Description |
---|---|
showMinimized | 最小化方式显示窗口 |
showMaximized | 最大化方式显示窗口 |
showFullScreen | 全屏方式显示窗口 |
showNormal | 正常方式显示窗口 |
上面4个既是函数, 又是槽
self.showMaximized() # 是函数, 也是槽
窗口的最大化, 还有这种方式可以调用
self.setWindowState(Qt.WindowMaximized)
Constant | Description |
---|---|
Qt.WindowNoState | 正常状态 |
Qt.WindowMinimized | 最小化状态 |
Qt.WindowMaxmized | 最大化状态 |
Qt.WindowFullScreen | 全屏状态 |
Qt.WindowActive | 激活状态 |
方法:
windowStates = self.windowState() # 获取状态
ismin = self.isMinimized() # 判断窗口是否为最小化
ismax = self.isMaximized() # 判断窗口是否为最大化
isfull = self.isFullScreen() # 判断窗口是否为全屏
禁用窗口:
禁用该窗口, 会使该窗口里所有控件处于不能被使用状态.
没错, 这是一个槽
Func & Slots | Description |
---|---|
setEnabled | 启用窗口(禁用) |
setDisabled | 禁用窗口(启用) |
上面2个方法既是函数, 也是槽, 但是效果缺是相反的, 尴尬…
self.setDisabled(True) # 启用该窗口 (与槽效果相反)
方法:
isenable = self.isEnabled() # 该窗口是否启用
isaction = self.isActiveWindow() # 判断该窗口是否激活
self.activateWindow() # 设置窗口为激活状态
窗口的可见
Func & Slots | Description |
---|---|
setVisible | 显示(隐藏) |
setHidden | 隐藏(显示) |
self.setVisible(True) # 设置可见 (与槽效果相反)
Slots | Description |
---|---|
show | 显示 |
hide | 隐藏 |
close | 关闭(返回 bool) |
lower | 降低窗口到最下面 |
raise | 提升窗口到最上面 |
方法:
isVisible = self.isVisible() # 窗口是否可见
isHidden = self.isHidden() # 窗口是否隐藏
self.setVisible(True) # 设置可见
self.setHidden(True) # 设置隐藏
还有几个可以复写的事件:
def closeEvent(self, QCloseEvent):'''关闭'''passdef showEvent(self, QShowEvent):'''显示'''passdef hideEvent(self, QHideEvent):'''隐藏'''passdef mouseMoveEvent(self, QMouseEvent):'''移动'''point_old = QMouseEvent.oldPos() # 旧坐标point_new = QMouseEvent.newPos() # 新坐标def resizeEvent(self, QResizeEvent):'''大小改变'''size_old = QResizeEvent.oldSize() # 旧大小size_new = QResizeEvent.newSize() # 新大小
焦点:
Func & Slots | Description |
---|---|
setFocus | 获得焦点 |
self.setFocus() # 使窗口获得焦点
方法
hashFocus = self.hasFocus() # 判断窗口是否获得焦点
self.clearFocus() # 使窗口失去焦点
widget = self.focusWidget() # 得到窗口内获得焦点的子窗口
他还有两个可以复写的方法
def focusInEvent(self, QFocusEvent):'''获得焦点'''passdef focusOutEvent(self, QFocusEvent):'''失去焦点'''pass
鼠标与键盘:
方法:
self.grabKeyboard() # 捕获键盘事件
self.releaseKeyboard() # 释放键盘事件self.grabMouse() # 捕获鼠标事件
self.releaseMouse() # 释放鼠标事件widget = self.keyboardGrabber() # 得到正在捕获键盘事件的窗口
widget = self.mouseGrabber() # 得到正在捕获鼠标事件的窗口
几个可复写的键盘事件
def keyPressEvent(self, QKeyEvent):'''键盘按下'''int_key = QKeyEvent.key() # 得到键值if int_key == Qt.Key_Escape:passdef keyReleaseEvent(self, QKeyEvent):'''键盘松开'''pass
几个可复写的鼠标事件:
def mousePressEvent(self, QMouseEvent):'''鼠标按下'''passpoint = QMouseEvent.pos() # 鼠标(相对)坐标int_x = QMouseEvent.x() # 鼠标(相对)x坐标int_y = QMouseEvent.y() # 鼠标(相对)y坐标point_g = QMouseEvent.globalPos() # 鼠标(全局)坐标int_gx = QMouseEvent.globalX() # 鼠标(全局)x坐标int_gy = QMouseEvent.globalY() # 鼠标(全局)y坐标mouseButton = QMouseEvent.button() # 引起事件的鼠标键mouseButtons = QMouseEvent.buttons() # 事件发生时的鼠标键状态 (|组合)'''Qt.MouseButton 的枚举 | Description--- | ---Qt.NoButton | 无键Qt.LeftButton | 左键Qt.RightButton | 右键Qt.MidButton | 中键'''if mouseButton == Qt.LeftButton:passdef mouseReleaseEvent(self, QMouseEvent):'''鼠标松开'''passdef mouseDoubleClickEvent(self, QMouseEvent):'''鼠标双击'''passdef mouseMoveEvent(self, QMouseEvent):'''鼠标移动 (默认需要按下)# 开启鼠标追踪(不需要按下)self.setMouseTracking(True)'''mouseButton = QMouseEvent.button() # Move事件 总是返回 Qt.NoButtondef enterEvent(self, QEvent):'''鼠标进入窗体'''passdef leaveEvent(self, QEvent):'''鼠标离开窗体'''passdef wheelEvent(self, QWheelEvent):'''鼠标滚轮滚动'''int_delta = QWheelEvent.delta() # 滚轮转动的角度orientation = QWheelEvent.orientationI() # 滚轮转动的方向'''Qt.Orientation 的枚举 | Description--- | ---Qt.Horizontal | 横向Qt.Vertical | 纵向'''if orientation == Qt.Horizontal:pass
Qt.MouseButton 的枚举 | Description |
---|---|
Qt.NoButton | 无键 |
Qt.LeftButton | 左键 |
Qt.RightButton | 右键 |
Qt.MidButton | 中键 |
Qt.Orientation 的枚举 | Description |
---|---|
Qt.Horizontal | 横向 |
Qt.Vertical | 纵向 |
上述的鼠标移动事件只有按下时才追踪, 如果想不按下就追踪, 则要加上下面这段代码
# 开启鼠标追踪(不需要按下)
self.setMouseTracking(True)
布局:
方法
self.setLayout(QLayout()) # 设置顶级布局
layout = self.layout() # 获得顶级布局
字体:
方法
self.setFont(QFont()) # 设置字体
font = self.font() # 获得字体
关于事件的处理:
def enterEvent(self, QEvent):# 事件的处理if True:QEvent.accept() # 接受事件else:QEvent.ignore() # 忽略事件# 事件被忽略后: 关闭事件 - 窗口将不会被关闭; 键盘、鼠标等输入事件 - 向上传播到父窗口pass
QToolTip 提示泡泡
设置泡泡字体
QToolTip.setFont(QFont('SansSerif', 10))
在需要提示的Widget上使用
self.setToolTip('This is a <b>QWidget</b> widget')
案例: 在Widget 和 PushButton 上添加提示泡泡
def initUI(self):# 设置字体QToolTip.setFont(QFont('SansSerif', 10))self.setToolTip('This is a <b>QWidget</b> widget')btn = QPushButton('Button', self)btn.setToolTip('This is a <b>QPushButton</b> widget')btn.resize(btn.sizeHint()) # sizeHint 为默认大小btn.move(50, 50)
效果:
QDialog 对话框
QDialog中还有几个与打印相关的对话框(QAbstractPrintDialog / QPageSetupDialog / QPrintDialog / QPrintPreviewDialog)全部放到打印里讲
QDialog
该Dialog是可以自己放控件来设计它的样子和作用, 当然系统也内置很多设计好的Dialog, 如: QColorDialog / QFileDialog / QMessageBox 等等.
Qt.WindowModality | Description |
---|---|
Qt.NonModal | 非模态, 可与其他窗口交互 |
Qt.WindowModal | 窗口模态, 未处理完当前对话框, 将阻止与对话框父窗口交互 |
Qt.ApplicationModal | 应用程序模态, 阻止和任何其他窗口交互 |
案例
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QDialog
from PyQt5.Qt import Qtclass MyDialog(QDialog):def __init__(self):super().__init__()self.initUI()def initUI(self):self.setWindowTitle("My Dialog")self.resize(200, 100)self.btn = QPushButton("ok", self)self.btn.clicked.connect(self.close)'''Qt.WindowModality | Description--- | ---Qt.NonModal | 非模态, 可与其他窗口交互Qt.WindowModal | 窗口模态, 未处理完当前对话框, 将阻止与对话框父窗口交互Qt.ApplicationModal | 应用程序模态, 阻止和任何其他窗口交互'''self.setWindowModality(Qt.ApplicationModal)# self.exec_()def text(self):return self.btn.text()@staticmethoddef getText():dialog = MyDialog()dialog.exec_()text = dialog.text()return text
使用的话也很简单:
text = MyDialog.getText()
print(text)
QMessageBox 消息盒子
消息盒子主要弹出: 提示 / 警告 / 错误 / 询问 / 关于 等等对话框, 不过仅仅是体现在显示的图标不同
主要方法:
主要方法:
msgBox.setWindowTitle("默认1") # 设置标题
msgBox.setText("The document has been modified.") # 主文本
msgBox.setInformativeText("Do you want to save your changes?") # 副文本
msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) # 按钮s
msgBox.setDefaultButton(QMessageBox.Save) # 默认(选中)按钮
msgBox.setDetailedText("If the detailed text property is set...") # 详情按钮
msgBox.setIcon(QMessageBox.Question) # 图标
msgBox.setIconPixmap(QPixmap('icon.png'))
ret = msgBox.exec_()# 上述的综合体
ret = QMessageBox.question(self, 'Question', "The document has been modified...", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
分别设置:
# 默认1
msgBox = QMessageBox()
msgBox.setWindowTitle("默认1")
msgBox.setText("The document has been modified.")
msgBox.exec_()# 默认2 (+按钮)
msgBox = QMessageBox()
msgBox.setWindowTitle("默认2")
msgBox.setText("The document has been modified.") # 主文本
msgBox.setInformativeText("Do you want to save your changes?") # 副文本
msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) # 按钮s
msgBox.setDefaultButton(QMessageBox.Save) # 默认(选中)按钮
ret = msgBox.exec_()if ret == QMessageBox.Save:pass
elif ret == QMessageBox.Discard:pass
elif ret == QMessageBox.Cancel:pass
else:pass# 默认3 (+DetailedText)
msgBox = QMessageBox()
msgBox.setWindowTitle("默认3")
msgBox.setText("The document has been modified.")
# 增加详情按钮
msgBox.setDetailedText("If the detailed text property is set, the Show Details… button will be shown.")
msgBox.exec_()# 默认4 (+icon)
msgBox = QMessageBox()
msgBox.setWindowTitle("默认4")
msgBox.setText("The document has been modified.")
msgBox.setIcon(QMessageBox.Question)
msgBox.exec_()
效果:
综合体:
对话框类型与图标
QMessageBox.Icon | icon |
---|---|
QMessageBox.NoIcon | - |
QMessageBox.Question | |
QMessageBox.Information | |
QMessageBox.Warning | |
QMessageBox.Critical |
支持的带类型的对话框, 除了NoIcon没有之外, 其余4个都有, 其效果与代码都是效果的, 就是改变图标而已.
外加一个 about, 显示没有图标, 代码没有按钮.
# 创建消息盒子 (父窗口 / 标题 / 内容 / 按钮s / 默认(选中)按钮)
ret = QMessageBox.question(self, 'Question', "The document has been modified.\nDo you want to save your changes?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
# 除了上述4种外(除NoIcon), 还有个 about
ret = QMessageBox.about(self, 'About', "The document has been modified.")
效果:
按钮:
QMessageBox.StandardButton | button name | Role |
---|---|---|
QMessageBox.Ok | “OK” | AcceptRole |
QMessageBox.Open | “Open” | AcceptRole |
QMessageBox.Save | “Save” | AcceptRole |
QMessageBox.Cancel | “Cancel” | RejectRole |
QMessageBox.Close | “Close” | RejectRole |
QMessageBox.Discard | “Discard” or “Don’t Save” | DestructiveRole |
QMessageBox.Apply | “Apply” | ApplyRole |
QMessageBox.Reset | “Reset” | ResetRole |
QMessageBox.RestoreDefaults | “Restore Defaults” | ResetRole |
QMessageBox.Help | “Help” | HelpRole |
QMessageBox.SaveAll | “Save All” | AcceptRole |
QMessageBox.Yes | “Yes” | YesRole |
QMessageBox.YesToAll | “Yes to All” | YesRole |
QMessageBox.No | “No” | NoRole |
QMessageBox.NoToAll | “No to All” | NoRole |
QMessageBox.Abort | “Abort” | RejectRole |
QMessageBox.Retry | “Retry” | AcceptRole |
QMessageBox.Ignore | “Ignore” | AcceptRole |
QMessageBox.NoButton | - | - |
中文按钮名:
默认的按钮名都是英文的, 若要中文显示则需要处理.
# 如果要中文按钮名, 则要自己添加按钮
msgBox = QMessageBox(self)
disconnectbtn = msgBox.addButton("中文按钮", QMessageBox.ActionRole)
abortbtn = msgBox.addButton(QMessageBox.Abort)
msgBox.exec_()
if msgBox.clickedButton() == disconnectbtn:pass
elif msgBox.clickedButton() == abortbtn:pass
效果:
Role | Description |
---|---|
QMessageBox.InvalidRole | 无效 |
QMessageBox.AcceptRole | 接受 |
QMessageBox.RejectRole | 拒绝 |
QMessageBox.DestructiveRole | 破坏性更改(如:放弃更改) |
QMessageBox.ActionRole | 更改对话框里的元素 |
QMessageBox.HelpRole | help |
QMessageBox.YesRole | Yes |
QMessageBox.NoRole | No |
QMessageBox.ApplyRole | 应用改变 |
QMessageBox.ResetRole | 重设 |
案例:
举个栗子, 我们在关闭窗口时弹出消息盒子.
def closeEvent(self, event):'''举个栗子, 我们在关闭窗口时弹出消息盒子.'''msgBox = QMessageBox(self)msgBox.setWindowTitle('关闭')msgBox.setText('是否保存?')msgBox.setIcon(QMessageBox.Question)btn_dontsave = msgBox.addButton('不保存', QMessageBox.AcceptRole)btn_cancel = msgBox.addButton('取消', QMessageBox.RejectRole)btn_save = msgBox.addButton('保存', QMessageBox.AcceptRole)msgBox.setDefaultButton(btn_save)msgBox.exec_()if msgBox.clickedButton() == btn_dontsave:print('不保存')event.accept()elif msgBox.clickedButton() == btn_cancel:event.ignore()elif msgBox.clickedButton() == btn_save:print('保存')event.accept()
效果:
QInputDialog 可输入对话框
输入对话框由 一个文本框 和 两个按钮组成, 用户点击ok(或 Enter 键), Dialog收集输入数据返回.
Constant | Description |
---|---|
QInputDialog.getText | 一行文本 |
QInputDialog.getMultiLineText | 多行文本 |
QInputDialog.getDouble | 浮点数 |
QInputDialog.getInt | 整数 |
QInputDialog.getItem | 条目选择 |
text, ok = QInputDialog.getText(self, 'getText', '请输入文本')
if ok and text:print(text)text, ok = QInputDialog.getMultiLineText(self, 'getMultiLineText', '请输入文本', "默认文本")
if ok and text:print(text)# 父控件 / 标题 / 文本 / 默认值 / 最小值 / 最大值 / 小数位数
double, ok = QInputDialog.getDouble(self, 'getDouble', '请输入浮点数', 37.56, -10000, 10000, 2)
if ok:print(double)# 父控件 / 标题 / 文本 / 默认值 / 最小值 / 最大值 / 步进
int, ok = QInputDialog.getInt(self, 'getInteger', '请输入整数', 25, 0, 100, 1)
if ok:print(int)items = ["Spring", "Summer", "Fall", "Winter"]
# 父控件 / 标题 / 文本 / list of strings 数据 / 默认值 / 可编辑
item, ok = QInputDialog.getItem(self, 'getItem', '请选择条目', items, 0, False)
if ok and item:print(item)
效果:
中文按钮名:
dialog = QInputDialog(self)
dialog.setWindowTitle('getText')
dialog.setLabelText('请输入文本')
dialog.setOkButtonText('确定')
dialog.setCancelButtonText('取消')
ok = dialog.exec_()
text = dialog.textValue()
if ok and text:print(text)
效果
QFontDialog 字体选择对话框
用户可以选择文本的字号大小 / 样式 / 格式 等.
def initUI(self):btn = QPushButton('打开字体选择对话框', self)btn.clicked.connect(self.showDialog)self.lbl = QLabel('Knowledge only matters', self)self.lbl.move(0, 50)self.setGeometry(300, 300, 550, 350)self.setWindowTitle('Dialog')self.show()def showDialog(self):font, ok = QFontDialog.getFont()if ok:self.lbl.setFont(font)self.lbl.adjustSize()
效果:
中文按钮及汉化:
使用系统翻译库吧
QFileDialog 文件对话框
用于文件的打开和保存, 可以打开指定扩展名的文件, 也可以设置起始目录.
Constant | Description |
---|---|
getOpenFileName() | 打开选择文件对话框 |
getOpenFileNames() | 打开选择多个文件对话框 |
getExistingDirectory() | 打开选择文件夹对话框 |
getSaveFileName() | 打开保存文件对话框 |
# (标题 / 默认路径['/home' | 'C:\我的电脑'] / 过滤文件类型)
# 路径: "."代表程序运行路径(默认) / "/"代表根目录(win+linux)
fname = QFileDialog.getOpenFileName(self, 'Open file', 'C:\我的电脑', 'Image files (*.jpg *.png *.gif)')
# ('C:/5560WIA.txt', 'All Files (*)')
# ('C:/我的电脑/xxx.png', 'Image files (*.jpg *.png *.gif)')
print(fname)files = QFileDialog.getOpenFileNames(self, "Select one or more files to open", ".", "Images (*.png *.xpm *.jpg)")dir = QFileDialog.getExistingDirectory(self, "Open Directory", ".", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks)fileName = QFileDialog.getSaveFileName(self, "Save File", "C:\我的电脑\\untitled.png", "Images (*.png *.xpm *.jpg)")
当然也可以分别设置:
dialog = QFileDialog()
dialog.setFileMode(QFileDialog.AnyFile)
if dialog.exec_():filename = dialog.selectedFiles()print(filename) # ['C:/Code/Python/PyQt5Demo2/icon.png']
效果:
窗口显示模式, 不过在win10上没效果
ViewMode | Description |
---|---|
QFileDialog.Detail | 详情(列表+其他信息) |
QFileDialog.List | 列表(文件名+列表) |
dialog.setViewMode(QFileDialog.Detail) # 设置视图模式 (win10没效果)
必选文件模式:
QFileDialog.FileMode | Description |
---|---|
QFileDialog.AnyFile | 不管是否存在 (适合保存) |
QFileDialog.ExistingFile | 单个文件名(适合打开) |
QFileDialog.Directory | 单个目录 (适合选择目录) |
QFileDialog.ExistingFiles | 1个或多个存在文件 (适合多个打开) |
dialog.setFileMode(QFileDialog.AnyFile) # 指定必选文件模式
过滤写法:
''' 过滤单个: Images (*.png *.xpm *.jpg)过滤多个: "Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)" '''
dialog.setNameFilter("Images (*.png *.xpm *.jpg)") # 设置过滤文件名类型
中文按钮及汉化
不存在该问题, 调用的是系统控件
QColorDialog 颜色选择对话框
self.frm = QFrame(self)
self.frm.setGeometry(10, 10, 100, 100)# 默认颜色 / 父控件 / 标题
col = QColorDialog.getColor(Qt.blue, self, '选择颜色')
if col.isValid():self.frm.setStyleSheet("QWidget { background-color: %s }" % col.name())
效果:
直接创建颜色:
col = QColor(0, 0, 0)
中文按钮及汉化:
使用系统翻译库吧
QProgressDialog 进度对话框
def initUI(self):self.min = 0self.max = 100self.progress = QProgressDialog("Copying files...", "Abort Copy", self.min, self.max, self)self.progress.setWindowModality(Qt.WindowModal)self.progress.canceled.connect(self.cancel)self.timer = QBasicTimer()self.step = 0if self.timer.isActive():self.timer.stop()else:self.timer.start(100, self)self.setGeometry(300, 300, 550, 350)self.setWindowTitle('Dialog')self.show()def timerEvent(self, e):if self.step >= self.max:self.timer.stop()return# ... copy one fileself.step = self.step + 1self.progress.setValue(self.step)def cancel(self):print("你Y的, 竟然取消了")self.timer.stop()
效果:
中文按钮及汉化:
全换成中文就好了
QErrorMessage 错误提示
# 方式一
err = QErrorMessage(self)
err.showMessage("这是显示的错误信息")# 方式二 默认输出错误信息, 没有则创建, 推荐该方法
QErrorMessage.qtHandler().showMessage("这里显示的错误信息")
show this message again
√去掉, 下次显示将不执行(不显示)
效果:
中文按钮及汉化:
使用系统翻译库吧
QWizard 向导窗
一般用在安装应用时, 或者第一次启动应用时.
向导窗由多个向导页(QWizardPage)组成, 通过以下addPage()
方法添加向导页到向导窗(QWizard)
class MyWizard(QWizard):def __init__(self):super().__init__()self.initUI()def initUI(self):self.addPage(WizardPage())self.setWizardStyle(QWizard.ClassicStyle)self.setWindowTitle("Trivial Wizard")# self.show()
QWizardPage向导页:
class WizardPage(QWizardPage):def __init__(self):super().__init__()self.initUI()def initUI(self):'''向导页'''self.setTitle("Introduction")label = QLabel("This wizard will help you register your copy of Super Product Two.")label.setWordWrap(True)layout = QVBoxLayout()layout.addWidget(label)self.setLayout(layout)
内容显示位置:
样式:
WizardStyle | Description |
---|---|
ClassicStyle | |
ModernStyle | |
MacStyle | |
AeroStyle |
self.wizard.setWizardStyle(QWizard.ClassicStyle)
虽然官方给的样式图是这样的, 但是实际显示效果则未必.
按条件调整展示向导页顺序
默认情况下是按id递增显示的, 我们可以重写QWizardPage.nextId()
提供动态的顺序.
设置id (使用.setPage()
来代替.addPage()
):
self.setPage(1, IntroPage())
self.setPage(2, EvaluatePage())
self.setStartId(1) # 开始页
重写QWizardPage.nextId()
:
def nextId(self):'''返回下一页的id'''if self.upgradeKeyLineEdit.text().isEmpty():return 1 # 没有下一页返回 -1else:return 2
按钮
QWizard.WizardButton | Description |
---|---|
QWizard.BackButton | 上一步 |
QWizard.NextButton | 下一步 |
QWizard.CommitButton | 提交 |
QWizard.FinishButton | 完成 |
QWizard.CancelButton | 取消 |
QWizard.HelpButton | 帮助 |
QWizard.CustomButton1 | 第1个自定义按钮 |
QWizard.CustomButton2 | 第2个自定义按钮 |
QWizard.CustomButton3 | 第3个自定义按钮 |
QWizard.Stretch | 在ButtonLayout中使用, 水平拉伸 |
按钮布局:
设置按钮水平的位置, 会与其他位置配置(如: .setOption())发生冲突, 而使其他位置配置失效.
layout = [QWizard.Stretch, QWizard.BackButton, QWizard.CancelButton, QWizard.NextButton, QWizard.FinishButton]
self.setButtonLayout(layout)
使用与不是QWizard.Stretch
的区别:
选择权:
QWizard.WizardOption | Description |
---|---|
QWizard.IndependentPages | 独立页面 |
QWizard.IgnoreSubTitles | 不显示任何子标题 |
QWizard.ExtendedWatermarkPixmap | 将任何WatermarkPixmap延伸到屏幕边缘 |
QWizard.NoDefaultButton | 不将Next和Finish设为默认按钮 |
QWizard.NoBackButtonOnStartPage | 不要在开始页显示Back按钮 |
QWizard.NoBackButtonOnLastPage | 不要在结束页显示Back按钮 |
QWizard.DisabledBackButtonOnLastPage | 禁用最后页Back按钮 |
QWizard.HaveNextButtonOnLastPage | 显示最后页Next(禁用)按钮 |
QWizard.HaveFinishButtonOnEarlyPages | 显示非最后页Finish(禁用)按钮 |
QWizard.NoCancelButton | 不显示Cancel按钮 |
QWizard.CancelButtonOnLeft | 将Cancel放在Back左侧 |
QWizard.HaveHelpButton | 显示Help按钮 |
QWizard.HelpButtonOnRight | 将Help放在按钮布局最右侧 |
QWizard.HaveCustomButton1 | 显示第1个自定义按钮 |
QWizard.HaveCustomButton2 | 显示第2个自定义按钮 |
QWizard.HaveCustomButton3 | 显示第3个自定义按钮 |
QWizard.NoCancelButtonOnLastPage | 不显示在最后页的Cancel按钮 |
self.setOption(QWizard.NoBackButtonOnStartPage, True)
自定义按钮:
def initUI(self):...self.setButtonText(QWizard.CustomButton1, "猜猜我是谁")self.setOption(QWizard.HaveCustomButton1, True)self.customButtonClicked.connect(self.printButtonClicked)def printButtonClicked(self, which):text = self.button(which).text()print(text)
中文按钮及汉化:
self.setButtonText(QWizard.FinishButton, '大功告成')
直接使用系统翻译即可
图片
设置图片:
QWizard.WizardPixmap | Description |
---|---|
QWizard.WatermarkPixmap | 显示在 ClassicStyle/ModernStyle 的左侧 |
QWizard.LogoPixmap | 显示在 ClassicStyle or ModernStyle 的页头右侧 |
QWizard.BannerPixmap | 显示在 ModernStyle 页头标题栏背景 |
QWizard.BackgroundPixmap | 显示在 MacStyle 的背景 |
使用时需结合样式才有效果
self.setPixmap(QWizard.WatermarkPixmap, QPixmap('background.png'))
设置控件:
将控件设置在向导的左侧, 有使用WatermarkPixmap (ClassicStyle / ModernStyle)时设置在水印上方, 其他样式或没有使用水印时, 设置在向导左侧.
btn = QPushButton()
btn.setText("按钮")layout = QVBoxLayout()
layout.addWidget(btn)widget = QWidget()
widget.setLayout(layout)
self.setSideWidget(widget)