QT 官网
QT for python
在Qt官网的介绍中,PyQt包括三个主要的基础模组QtWidgets、QtCore和QtGui
PySide是Qt在Python的绑定,是将C++开发环境下的Qt移植到Python环境下。由于Python 语句简单,用Python语言开发Qt应用程序就变得相对容易。
下面内容是PySide几个主要模块的简介,其中 QtWidgets、QtCore和QtGui 是基本模块,开发GUI时都会用这三个模块,其他模块是扩展模块。
模块 | 描述 |
---|---|
QtBluetooth |
蓝牙API提供支持蓝牙的设备之间的连接。 |
QtCharts |
提供一组易于使用的图表组件。 |
QtConcurrent |
提供高级API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。 |
QtCore |
提供核心非GUI功能。 |
QtDataVisualization |
提供一种将3D数据可视化为条形图、散点图和曲面图的方法。 |
QtDBus |
D-Bus是一种进程间通信(IPC)和远程过程调用(RPC)机制,最初是为Linux开发的,旨在用一个统一的协议取代现有和竞争的IPC解决方案 |
QtDesigner |
提供类以扩展Qt设计器。 |
QtGui |
使用GUI功能扩展QtCore。 |
QtHelp |
提供用于在应用程序中集成在线留档的类。 |
Qt Multimedia |
为特定于多媒体的用例提供API。 |
Qt Multimedia Widgets |
提供基于小部件的多媒体API。 |
QtNetwork |
提供允许您编写TCP/IP客户端和服务器的类。 |
Qt Network Authorization |
提供一组API,使Qt应用程序能够在不暴露用户密码的情况下获得对在线帐户和HTTP服务的有限访问。 |
QtNfc |
NFC API提供支持NFC的设备之间的连接。 |
QtOpenGL |
提供可以轻松在Qt应用程序中使用OpenGL的类。 |
QtOpenGL Widgets |
提供OpenGLWidget类,为小部件树的某个部分启用OpenGL呈现。 |
Qt Positioning |
提供对位置、卫星信息和区域监控类的访问。 |
Qt PDF |
用于呈现PDF文档的类和函数。 |
Qt PDF Widgets |
PDF查看器小部件。 |
QtPrintSupport |
为打印提供广泛的跨平台支持。 |
QtQml |
Qt QML的Python API。 |
QtQuick |
提供用于在Qt应用程序中嵌入Qt Quick的类。 |
QtQuickControls2 |
提供用于从C++设置控件的类。 |
QtQuickWidgets |
提供QQuickWidget类,用于在基于小部件的应用程序中嵌入Qt Quick。 |
QtRemoteObjects |
为Qt开发的进程间通信(IPC)模块。该模块扩展了Qt的现有功能,以轻松实现进程或计算机之间的信息交换。 |
Qt Scxml |
提供用于从SCXML文件创建和使用状态机的类。 |
Qt Sensors |
提供对传感器硬件的访问。 |
Qt Serial Bus |
提供对串行工业总线接口的访问。目前,该模块支持CAN总线和Modbus协议。 |
Qt Serial Port |
提供与硬件和虚拟串行端**互的类。 |
Qt Spatial Audio |
提供用于在3D空间中建模声源及其周围环境的API。 |
QtSql |
帮助您为Qt应用程序提供无缝的数据库集成。 |
QtStateMachine |
提供用于创建和执行状态图的类。 |
QtSvg |
提供用于显示SVG文件内容的类。 |
QtSvgWidgets |
提供用于显示SVG文件内容的小部件。 |
QtTest |
为单元测试Qt应用程序和库提供类。 |
QtUiTools |
提供类来处理使用Qt Designer创建的表单。 |
Qt WebChannel |
从超文本标记语言客户端提供对QObject或QML对象的访问,以便将Qt应用程序与超文本标记语言/JavaScript客户端无缝集成。 |
QtWebEngine Core C++ Classes |
提供由QtWebEngine和QtWebEngine Widgets共享的公共API。 |
QtWebEngine Widgets C++ Classes |
提供C++类,用于在基于QWidget的应用程序中呈现Web内容。 |
QtWebEngine QML Types |
提供QML类型以在QML应用程序中呈现Web内容。 |
Qt WebSockets |
提供符合RFC 6455的WebSocket通信。 |
QtWidgets |
使用C++小部件功能扩展Qt GUI。 |
QtXml |
提供DOM的C++实现。 |
Qt3DAnimation |
提供动画3D对象所需的基本元素。 |
Qt3D Core |
包含支持近实时仿真系统的功能。 |
Qt3D Extras |
提供一组预构建元素来帮助您开始使用Qt 3D。 |
Qt3D Input |
提供用于在使用Qt 3D的应用程序中处理用户输入的类。 |
Qt3D Logic |
启用与Qt 3D后端同步帧。 |
Qt3D Render |
包含使用Qt 3D支持2D和3D渲染的功能。 |
Qt CoAP |
实现由RFC 7252定义的CoAP的客户端。 |
Qt OPC UA |
工业应用中数据建模和数据交换的协议。 |
Qt MQTT |
提供MQTT协议规范的实现。 |
翻译信息:
本文翻译自 PySide6 官方文档 The Meta-Object System
协议:本翻译遵守原文档使用的GNU Free Documentation License version 1.3授权
译者:muzing
翻译时间:2022.06
译者注:本文原文由Qt 6(C++)文档直接转换而来,似乎代码部分并未完全替换为有效的 Python 代码,建议与 C++ 原版文档对比阅读
Qt 的元对象系统(meta-object system)和自省(introspection)功能的概述。
Qt 的元对象系统为对象间通信、运行时类型信息和动态属性系统提供了信号与槽机制。
元对象系统基于三件事:
1.QObject
类为可以利用元对象系统的对象提供基类。
2.类声明私有部分中的 Q_OBJECT
宏用于启用元对象特性,例如动态属性、信号和槽。
3.元对象编译器(moc
)为每个 QObject
子类提供实现元对象功能所需的代码。
moc
工具读取 C++ 源文件。如果它找到一个或多个包含 Q_OBJECT
宏的类声明,它会生成另一个 C++ 源文件,其中包含每个类的元对象代码。这个生成的源文件要么是 #include
被包含在类的源文件中,要么(更常见地)是被编译并与类的实现链接。
除了提供对象间通信的信号与槽机制(这是引入系统的主要原因),元对象代码还提供以下附加功能:
metaObject()
返回类的关联 meta-object
className()
在运行时将类名作为字符串返回,无需通过 C++ 编译器支持本机运行时类型信息(RTTI)inherits()
函数返回对象是否继承 QObject
继承树中指定类的类的实例tr()
翻译字符以进行国际化setProperty()
和 property()
按名称动态设置和获取属性newInstance()
构造一个新的类实例也可以在 QObject
类上使用qobject_cast()
执行动态转换。qobject_cast()
函数的行为类似于 标准 C++ 的 dynamic_cast()
,其优点是不需要 RTTI 支持,并且可以跨动态库边界工作。它尝试将其参数转换为尖括号中指定的指针类型,如果对象的类型正确(在运行时确定),则返回非零指针;如果对象的类型不兼容,则返回 None
。
例如,假设 MyWidget
继承自 QWidget
并使用Q_OBJECT
宏声明:
obj = MyWidget()
QObject *
类型的 obj
变量实际上是指 MyWidget
对象,所以我们可以适当地转换它:
widget = QWidget(obj)
成功从 QObject
转换到 QWidget
,因为对象其实是一个 MyWidget
,它是 QWidget
的子类。由于我们知道 obj
是一个 MyWidget
,所以也可以将其转换为 MyWidget *
:
myWidget = MyWidget(obj)
转换至 MyWidget
成功,因为 qobject_cast()
没有区分内置 Qt 类型和自定义类型。
label = QLabel(obj)
# label is 0
另一方面,转换为 QLabel
失败。指针然后被设置为 0。这使得可以在运行时根据类型以不同的方式处理不同类型的对象:
if(QLabel label = QLabel(obj)){ label.setText(tr("Ping"))
} else if(QPushButton button = QPushButton(obj)){
button.setText(tr("Pong!"))
虽然可以在没有 Q_OBJECT
宏和元对象代码的情况下使用QObject
作为基类,但如果不使用Q_OBJECT
宏,则信号和槽、以及此处描述的其他功能都将不可用。从元对象系统的角度来看,没有元代码的 QObject
子类等价于最接近的具有元对象代码的祖先 。这意味着,例如,className()
不会返回类的实际名称,而是这个祖先的类名。
因此,我们强烈建议 QObject
的所有子类都使用Q_OBJECT
宏,无论它们是否真的使用信号、槽和属性。
翻译信息:
本文翻译自 Qt6 官方文档 Object Model
协议:本翻译遵守原文档使用的GFDLv1.3授权
译者:muzing
翻译时间:2022.06
标准 C++ 对象模型为对象范例提供了非常有效的运行时支持。但它的静态性质在某些具体问题领域不够灵活。GUI 编程是一个需要运行时效率和高灵活性的领域。Qt 通过将 C++ 的速度与 Qt 对象模型的灵活性相结合,来实现这一点。
Qt 为 C++ 添加了如下特性:
这些 Qt 特性中的许多是基于从QObject的继承,用标准 C++ 技术实现。其他的,比如对象通信机制和动态属性系统,需要由 Qt 自带的元对象编译器(moc)提供的元对象系统。
元对象系统是一种 C++ 扩展,使得该语言更适合真正的组件 GUI 编程。
这些类构成了 Qt 对象模型的基础。
QMetaClassInfo | 关于类的附加信息 |
---|---|
QMetaEnum | 关于枚举器的元数据 |
QMetaMethod | 关于成员函数的元数据 |
QMetaObject | 包含有关 Qt 对象的元信息 |
QMetaProperty | 关于属性的元数据 |
QMetaSequence | 允许对顺序容器进行类型擦除访问 |
QMetaType | 管理元对象系统中的具名类型 |
QObject | 所有 Qt 对象的基类 |
QObjectCleanupHandler | 监视多个 QObject 的生命周期 |
QPointer | 模板类,提供受保护的指向 QObject 的指针 |
QSignalBlocker | 包裹 QObject::blockSignals()的异常安全包装器 |
QSignalMapper | 绑定来自可识别发送者的信号 |
QVariant | 行为类似于最常见的 Qt 数据类型的集合 |
上面列出的 Qt 对象模型的一些附加功能要求我们将 Qt 对象视为身份(identities),而不是值(values)。值被复制或分配,身份被克隆。克隆意味着创建一个新的身份,而不是旧身份的精确复制品。例如,双胞胎有不同的身份。他们可能看起来相同,但名称不同、位置不同,并且可能有完全不同的社交圈子。
克隆身份是比复制或分配更复杂的操作。我们可以在 Qt 对象模型中看到这意味着什么。
一个 Qt 对象……
出于这些原因,Qt 对象应该被视为身份而不是值。身份是克隆的,而不是复制或分配的,克隆身份是比复制或分配值更复杂的操作。因此,QObject及其所有直接或间接继承的子类,都被禁用了复制构造函数和赋值运算符。
QObjects在对象树(object trees)中组织自身。当创建一个以另一个对象为父对象的QObject时,它会被添加到父对象的children()列表中,并在父对象被销毁时销毁。事实证明,这种方式非常适合 GUI 对象的需求。例如,一个QShortcut(键盘快捷键)是相关窗口的子对象,因此当用户关闭该窗口时,快捷键也会被销毁。
QQuickItem是 Qt Quick 模块的基本视觉元素,继承自QObject,但有一个与 QObject父对象不同的视觉父项的概念。一个项目的视觉父项可能并不是其父对象。参阅Concepts - Visual Parent in Qt Quick获取更多详细信息。
QWidget,即 Qt Widgets 模块的基础类,扩展了父子关系。一个普通子对象也成为一个子控件,也就是说,它会被显示在父级的坐标系中,并被其父级的边界按图形方式裁剪。例如,当应用程序在关闭消息框后销毁它时,正如我们所希望的那样,消息框的按钮和标签也被销毁,这是因为按钮和标签是消息框的子控件。
您也可以自行删除子对象,它们会自动从其父控件中移除自己。例如,当用户移除一项工具栏时,可能会导致应用程序删除其QToolBar对象之一,在这种情况下,工具栏的父对象QMainWindow将检测到变化,并相应地重新配置其屏幕空间。
当应用程序视觉上或行为上表现异常时,调试函数QObject::dumpObjectTree()和QObject::dumpObjectInfo()通常很有用。
当QObjects在堆上创建(即,使用new 创建)时,可以以任意顺序从它们构建对象树,稍后,可以以任意顺序销毁树中的对象。当任何QObjects被删除时,如果该对象有父对象,则析构函数会自动从其父对象中删除该对象。如果该对象有子对象,则析构函数会自动删除每个子对象。不管销毁的顺序,没有QObjects会被删除两次。
当QObjects在栈上创建时,适用相同的行为。通常,破坏顺序仍不会造成问题。考虑一下代码段:
int main()
{
QWidget window;
QPushButton quit("Quit",&window);
...
}
父对象 window
和子对象 quit
都是QObjects,因为QPushButton继承自QWidget,QWidget又继承自QObjects。这段代码是正确的:quit
的析构函数没有被调用两次,因为 C++ 语言标准 (ISO/IEC 14882:2003) 指定本地对象的析构函数按其构造函数的相反顺序调用。因此,先调用子对象 quit
的析构函数,然后将自己从其父对象 window
中移除,再调用window
的析构函数。
但是考虑一下如果交换构造顺序会发生什么,如第二个片段所示:
int main()
{
QPushButton quit("Quit");
QWidget window;
quit.setParent(&window);
...
}
在这种情况下,破坏顺序会引发问题。首先调用父对象的析构函数,因为它是最后被创建的。然后调用其子对象 quit
的析构函数,但这是不正确的,因为 quit
是一个局部变量。当 quit
随后超出作用域时,其析构函数再次被调用,这一次是正确的,但已经发生了破坏。
所有可视控件都继承自QWidget类
可利用__subclasses__
魔法方法获取各个组件的继承关系
# -*- coding: UTF-8 -*-
# File date: Hi_2023/1/14 19:17
# File_name: 00_查看组件继承关系.py
def get_direct_inherited(widget):
"""获取组件被直接继承的子类,不包含多层继承的"""
return widget.__subclasses__()
def get_inherit_subclass(widget):
"""获取组件被继承以及多层继承的所有子类"""
all_inherit_list = list()
def get_subclass(cls):
return cls.__subclasses__()
def get_widget_all_inherit(_subclass):
nonlocal all_inherit_list
if get_subclass(_subclass)!= 0:
for subclass in get_subclass(_subclass):
all_inherit_list.append("-----------------")
all_inherit_list.append(subclass)
get_widget_all_inherit(subclass)
cls_inherit_list = get_subclass(widget)
for subclass_inherit in cls_inherit_list:
all_inherit_list.append(subclass_inherit)
get_widget_all_inherit(subclass_inherit)
return all_inherit_list
if __name__ =='__main__':
from PySide6.QtGui import QValidator
print(get_direct_inherited(QValidator))
# get_inherit_subclass(QWidget)
QtCore.Qt 命名空间下包含了整个 Qt 库中所使用的各种标识符。
这些标识符大多为枚举(enum)或标志(flags)类型。本文收录部分标识符文档的中文翻译,按字母顺序排列。
Qt.AlignmentFlag 中又分为水平对齐方式与垂直对齐方式,具体有如下数种:
水平对齐:
常量 | 值 | 描述 |
---|---|---|
Qt.AlignLeft | 0x0001 | 与左边缘对齐 |
Qt.AlignRight | 0x0002 | 与右边缘对齐 |
Qt.AlignHCenter | 0x0004 | 在可用空间中水平居中 |
Qt.AlignJustify | 0x0008 | 两端对齐(尽可能使文字占满横向空间) |
垂直对齐:
常量 | 值 | 描述 |
---|---|---|
Qt.AlignTop | 0x0020 | 与顶部对齐 |
Qt.Alignbottom | 0x0040 | 与底部对齐 |
Qt.AlignVCenter | 0x0080 | 在可用空间中垂直居中 |
Qt.AlignBaseline | 0x0100 | 与基线对齐 |
若需同时设置水平、垂直两个维度的对齐方式,只需将两个Flags用或运算符连接,例如:
Qt.AlignCenter
等价于 Qt.AlignVCenter | Qt.AlignHCenter
此枚举值描述文本光标移动的风格。逻辑风格中,键盘左箭头意味着光标向文本的前方移动(对于从右至左的文本,前方意味着右方);而视觉风格中,键盘左箭头意味着光标向视觉上的左侧移动,而不考虑文本书写方向。
常量 | 值 | 描述 |
---|---|---|
Qt.LogicalMoveStyle | 0 | 在从左至右的文本块内,按下键盘左方向键时减少光标位置,右方向键增加光标位置;在从右向左的文本块内相反。 |
Qt.VisualMoveStyle | 1 | 无论书写方向如何,按下键盘左方向键光标总会向左移动,按下右方向键光标向右移动。 |
常量 | 值 | 描述 |
---|---|---|
Qt.TabFocus | 0x1 | 通过键盘Tab键获取焦点 |
Qt.ClickFocus | 0x2 | 通过鼠标点击获取焦点 |
Qt.StrongFocus | TabFocus | ClickFocus | 0x8 | 通过键盘Tab或鼠标点击获取焦点 |
Qt.WheelFocus | StrongFocus | 0x4 | 在StrongFocus基础上,还支持鼠标滚轮滚动获取焦点 |
Qt.NoFocus | 0 | 该控件不接受焦点,QLabel等不需要用户键盘操作的控件的默认值 |
控制 Qt 的布局与文字方向。
常量 | 值 | 描述 |
---|---|---|
Qt.LeftToRight | 0 | 从左至右布局 |
Qt.RightToLeft | 1 | 从右至左布局 |
Qt.LayoutDirectionAuto | 2 | 自动布局 |
对于阿拉伯语、希伯来语等特定语言,需要从右至左布局。
此枚举类型描述了QAbstractScrollArea滚动条的各种模式。水平滚动条与垂直滚动条的模式相互独立。
常量 | 值 | 描述 |
---|---|---|
Qt.ScrollBarAsNeeded | 0 | 只有当内容太大而无法容纳时,QAbstractScrollArea 才显示滚动条。此为默认值。 |
Qt.ScrollBarAlwaysOff | 1 | QAbstractScrollArea 永不显示滚动条。 |
Qt.ScrollBarAlwaysOn | 2 | QAbstractScrollArea 总显示一个滚动条。此属性在具有瞬态滚动条的操作系统上被忽略。 |
此枚举值指定显示需省略的文本时省略号应出现的位置:
常量 | 值 | 描述 |
---|---|---|
Qt.ElideLeft | 0 | 省略号应出现在文本的开头。 |
Qt.ElideRight | 1 | 省略号应出现在文本的末尾。 |
Qt.ElideMiddle | 2 | 省略号应出现在文本的中间。 |
Qt.ElideNone | 3 | 省略号不应出现在文本中。 |
Qt.ElideMiddle
通常是最适合 URL 的选择(例如,“http://bugreports.qt…/QTWEBSITE-13/”),而 Qt.ElideRight
适合其他字符串。
常量 | 值 | 描述 |
---|---|---|
Qt.PlaintText | 0 | 将文本字符串解析为纯文本 |
Qt.RichText | 1 | 将文本字符串解析为富文本 |
Qt.Autotext | 2 | 自动识别为纯文本或富文本 |
Qt.MarkdownText | 3 | 将文本字符串解析为Markdown格式的文本 |
常量 | 值 | 描述 |
---|---|---|
Qt.NotextInteraction | 0 | 不能与文本进行交互 |
Qt.TextSelectableByMouse | 1 | 可以使用鼠标选择文本,并用上下文菜单或标准键盘快捷键复制到剪贴板 |
Qt.TextSelectableByKeyboard | 2 | 可以用键盘上的光标键选择文本,会显示一个文本光标 |
Qt.LinksAccessibleByMouse | 4 | 链接高亮显示,并可用鼠标激活 |
Qt.LinksAccessibleByKeyboard | 8 | 链接可以使用Tab键获得焦点,并通过Enter键激活 |
Qt.TextEditable | 16 | 文本完全可编辑 |
Qt.TextEditorInteraction | TextSelectableByMouse | TextSelectableByKeyboard | TextEditable | 文本编辑器的默认值 |
Qt.TextBrowserInteraction | TextSelectableByMouse | LinksAccessibleByMouse | LinksAccessibleByKeyboard | QTextBrowser的默认值 |
此枚举值用于控制窗口的模态行为。对话框窗口大多为模态窗口。
常量 | 值 | 描述 |
---|---|---|
Qt.NonModal | 0 | 窗口为非模态,不阻塞其他窗口的输入 |
Qt.WindowModal | 1 | 窗口对单个窗口结构层次为模态,阻塞对其父窗口(及其的兄弟窗口)、祖父窗口(及其兄弟窗口)的输入 |
Qt.ApplicationModal | 2 | 窗口对应用程序为模态,阻塞对所有窗口的输入 |
此枚举值用于为控件指定各种窗口系统(window-system)属性。它们一般比较少见,但在少数情况下是必要的。其中一些标志取决于底层窗口管理器是否支持。
主要类型包括:
常量 | 值 | 描述 |
---|---|---|
Qt.Widget | 0x00000000 | QWidget的默认类型。这种类型的控件如果有父控件则作为子控件,若没有父控件则为独立窗口。参见 Qt.Window 和 Qt.SubWindow。 |
Qt.Window | 0x00000001 | 表示该控件是一个窗口,不管该控件是否有父控件,一般带有一个窗口系统框架和一个标题栏。注意如果控件没有父对象,则无法取消设置此标志。 |
Qt.Dialog | 0x00000002 | Window | 表示该控件是一个应装饰为对话框的窗口(即,一般在标题栏中没有最大化最小化按钮)。这是QDialog的默认类型。如果想用它作为模态对话框,它应该从另一个窗口启动,或者有父窗口并与QWidget.windowModality属性一起使用。如果将其设置为模态,对话框将阻止应用程序中的其他顶级窗口获得任何输入。我们将具有父控件的顶级窗口称为次要窗口(secondary window)。 |
Qt.Sheet | 0x00000004 | Window | 表示窗口是 macOS 上的 sheet。由于使用sheet 意味着窗口模式,因此推荐的方法是使用QWidget.setWindowModality()或QDialog::open()替代。 |
Qt.Popup | 0x00000008 | Window | 表示该控件是一个弹出式顶级窗口,即它是模态的,但具有适合弹出式菜单的窗口系统框架。 |
Qt.Tool | Popup | Dialog | 表示该控件是一个工具窗口。工具窗口通常是一个小窗口,具有比一般窗口更小的标题栏和装饰,一般用于工具按钮的集合。 如果有父控件,则工具窗口将始终保留在其顶部。如果没有父级,也可以考虑使用Qt.:WindowStaysOnTopHint。如果窗口系统支持,工具窗口可以用更轻量的框架来装饰。 它也可以与 Qt.:FramelessWindowHint 结合使用。在 macOS 上,工具窗口对应于窗口的NSPanel类。这意味着窗口位于普通窗口之上,因此无法在其上层放置普通窗口。 默认情况下,当应用程序处于非活动状态时,工具窗口将消失。这可以通过Qt.WA_MacAlwaysShowToolWindow属性来控制。 |
Qt.ToolTip | Popup | Sheet | 表明该控件是工具提示。这在内部用于实现工具提示。 |
Qt.SplashScreen | ToolTip | Dialog | 表明该窗口是闪屏(splash screen)。这是QSplashScreen的默认类型。 |
Qt.SubWindow | 0x00000012 | 表明此控件是子窗口,例如QMdiSubWindow控件。 |
Qt.ForeignWindow | 0x00000020 | Window | 表明此窗口对象是一个句柄,表示由另一个进程或手动使用本地代码创建的本地平台窗口。 |
Qt.CoverWindow | 0x00000040 | Window | 表示该窗口代表一个覆盖窗口,在某些平台上最小化应用程序时显示。 |
还有许多标志可用于自定义顶级窗口的外观。这对其他窗口没有影响:
常量 | 值 | 描述 |
---|---|---|
Qt.MSWindowsFixedSizeDialogHint | 0x00000100 | 在微软 Windows 上为窗口提供一个细对话框边框。这种风格传统上用于固定大小的对话框。注意:不建议在多显示器环境中使用此标志,因为系统将强制窗口在跨屏幕移动时保持其原始大小,这在使用具有不同分辨率的显示器时尤其不受欢迎。 |
Qt.MSWindowsOwnDC | 0x00000200 | 在微软 Windows 上为窗口提供自己的显示上下文。 |
Qt.BypassWindowManagerHint | 0x00000400 | 此标志可用于向平台插件指示应禁用"所有"窗口管理器协议。根据应用程序运行的操作系统和窗口管理器运行的情况,该标志的行为会有所不同。该标志可用于获取未设置配置的本机窗口。 |
Qt.X11BypassWindowManagerHint | BypassWindowManagerHint | 完全绕过窗口管理器。这会导致一个完全不受管理的无边框窗口(即,除非手动调用QWidget.activateWindow(),否则没有键盘输入)。 |
Qt.FramelessWindowHint | 0x00000800 | 生成无边框窗口。用户不能通过窗口系统移动或调整无边框窗口的大小。在 X11 上,标志的结果取决于窗口管理器及其理解 Motif 和/或 NETWM 的能力。大多数现有的现代窗口管理器都可以处理这个问题。 |
Qt.NoDropShadowWindowHint | 0x40000000 | 禁用在支持的平台上的窗口投影。 |
Qt.CustomizeWindowHint | 0x02000000 | 关闭默认窗口标题 hints。 |
Qt.WindowTitleHint | 0x00001000 | 为窗口添加标题栏。 |
Qt.WindowSystemMenuHint | 0x00002000 | 为窗口添加系统菜单,很可能是一个关闭按钮。如果想要隐藏/显示关闭按钮,更好的做法是使用WindowCloseButtonHint。 |
Qt.WindowMinimizeButtonHint | 0x00004000 | 为窗口添加最小化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。 |
Qt.WindowMaximizeButtonHint | 0x00008000 | 为窗口添加最大化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。 |
Qt.WindowMinMaxButtonsHint | WindowMinimizeButtonHint | WindowMaximizeButtonHint | 为窗口添加最大化、最小化按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。 |
Qt.WindowCloseButtonHint | 0x08000000 | 为窗口添加关闭按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。 |
Qt.WindowContextHelpButtonHint | 0x00010000 | 为对话框添加上下文帮助按钮。在某些平台上,这意味着 WindowSystemMenuHint 也已生效。 |
Qt.MacWindowToolBarButtonHint | 0x10000000 | 在 macOS 上添加一个工具栏按钮(即,在有工具栏的窗口的右上方的椭圆形按钮) |
Qt.WindowFullscreenButtonHint | 0x80000000 | 在 macOS 上添加一个全屏按钮 |
Qt.BypassGraphicsProxyWidget | 0x20000000 | 如果父控件已经嵌入,则阻止窗口及其子窗口自动将自己嵌入到 QGraphicsProxyWidget 中。如果希望控件始终是桌面上的顶级控件,则可以设置此标志,无论父控件是否已嵌入场景中。 |
Qt.WindowShadeButtonHint | 0x00020000 | 如果底层窗口管理器支持,则添加一个阴影按钮替代最小化按钮。 |
Qt.WindowStaysOnTopHint | 0x00040000 | 通知窗口系统该窗口应位于所有其他窗口之上。注意,在某些基于 X11 的窗口管理器上,还必须传递 Qt.X11BypassWindowManagerHint 才能使此标志正常工作。 |
Qt.WindowStaysOnbottomHint | 0x04000000 | 通知窗口系统该窗口应位于所有其他窗口之下。 |
Qt.WindowTransparentForInput | 0x00080000 | 通知窗口系统该窗口仅用于输出(显示某些内容)而不接受输入。因此输入事件应该像不存在一样略过。 |
Qt.WindowOverridesSystemGestures | 0x00100000 | 通知窗口系统该窗口实现了自己的一组手势,系统级的手势(例如三指切换屏幕)应当被禁用。 |
Qt.WindowDoesnotAcceptFocus | 0x00200000 | 通知窗口系统该窗口不接受输入焦点。 |
Qt.MaximizeUsingFullscreenGeometryHint | 0x00400000 | 通知窗口系统在最大化窗口时应尽可能多地使用可用的屏幕几何空间,包括可能被UI覆盖的区域(例如状态栏或应用程序启动器)。这可能会导致窗口被置于这些系统UI之下,具体情况取决于平台是否支持。启用该标志后,用户负责将QScreen.availableGeometry()也考虑在内,以便应用程序中需要用户交互的任何UI元素都不会被系统UI覆盖。 |
Qt.WindowType_Mask | 0x000000ff | 用于从窗口标志中提取窗口类型的掩码。 |
PySide6的窗口类主要有三种,分别为QWidget、QMainWindow 和QDialog,其中QMainWindow 和 QDialog 从QWidget类继承而来。
要创建和显示窗口,需要用这3个类中的任意一个类实例化对象,并让窗口对象显示并运行起来。
窗口类在PySide6的QtWidgets 模块中,使用窗口类之前,需要用"from PySide6.QtWidgets import QWidget,QMainWindow,QDialog"语句把它们导人进来。
下面的代码创建一个空白的QWidget 窗口,读者需要理解这段代码,这是整个PySide6
可视化编程最基础的知识。
import sys
from PySide6.QtWidgets import QApplication,QWidget
app = QApplication(sys.argv)# 创建应用程序实例对象
myWindow = QWidget()# 创建窗口实例对象
myWindow.show()# 显示窗口
n = app.exec()# 执行 exec()方法,进入事件循环,若遇到窗口退出命令,返回整数工
sys.exit(n)# 通知Python系统,结束程序运行
1.第1行导入系统模块sys,这个系统模块是指Python系统,而不是操作系统。
2.第2行导入QApplication 类和 QWidget 类,PySide6 的类都是以大写字母"Q"开始。
3.第4行创建OAnnlicatinn 类的实例对象ann.为窗口的创建进行初始化、其中svs.
4.argv是字符串列表,记录启动程序时的程序文件名和运行参数,可以通过print(sys.argv)函数输出sys.argv的值,sys.argv的第1个元素的值是程序文件名及路径,也可以不输入参数sys.argv 创建 QApplication 实例对象app。QApplication可以接受的两个参数是-nograb 和-dograb,-nograb告诉Python禁止获取鼠标和键盘事件,dograb则忽略-nograb选项功能,而不管-nograb参数是否存在于命令行参数中。一个程序中只能创建一个QApplication 实例,并且要在创建窗口前创建。
5.第5行用不带参数的QWidget 类创建QWidget 窗口实例对象myWindow,该窗口是独立窗口,有标题栏。
6.第6行用show()方法显示窗口,这时窗口是可见的。
7.第7行执行QApplication实例对象的exec()方法,开始窗口的事件循环,从而保证窗口一直处于显示状态。如果窗口上有其他控件,并为控件的消息编写了处理程序,则可以完成相应的动作。如果用户单击窗口右上角的关闭窗口按钮×正常退出界面,或者因程序崩溃而非正常终止窗口的运行,都将引发关闭窗口(closeA1lWindows())事件,这时app的方法exec()会返回一个整数,如果这个整数是0表示正常退出,如果非0表示非正常退出。请注意,当执行到app的exec()方法时,会停止后续语句的执行,直到所有可视化窗体都关闭(退出)后才执行后续的语句。需要注意的是,还有一个与exec()方法功能相同的方法exec_(),但exec_()方法已过时。
8.第8行调用系统模块的exit()方法,通知Python解释器程序已经结束,如果是sys.exit(0)状态,则Python 认为是正常退出;如果不是sys.exit(0)状态,则Python 认为是非正常退出。无论什么情况,sys.exit()都会抛出一个异常SystemExit,这时可以使用try…except语句捕获这个异常,并执行except中的语句,例如清除程序运行过程中的临时文件;如果没有try…except语句,则 Python解释器终止 sys.exit()后续语句的执行。第7行和第8行可以合并成一行sys.exit(app.exec())来执行。运行上面的程序,会得到一个窗口,这还只是一个空白窗口,在窗口上没有放置任何控件。
9.第78行可以这么简写sys.exit(app.exec())
# -*- coding: UTF-8 -*-
# File date: Hi_2023/1/30 23:04
# File_name: 05-继承写法.py
import sys
from PySide6.QtWidgets import QApplication,QWidget,QLabel
class MyWindow(QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.set_windows()
self.setupUi()
def set_windows(self):
self.resize(300,300)# 设置窗口大小
self.setWindowTitle("hello world")# 设置窗口标题
def setupUi(self):
qlabel = QLabel(self)
qlabel.setText("hello world")# 设置label文字
qlabel.move((self.width()- qlabel.width())// 2,(self.height()- qlabel.height())// 2)# 居中展示文本
if __name__ =='__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())
<?xml version="1.0"encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow"name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>480</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget"name="centralwidget">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
<widget class="QMenuBar"name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>640</width>
<height>22</height>
</rect>
</property>
<widget class="QMenu"name="menu">
<property name="title">
<string>菜单栏</string>
</property>
<widget class="QMenu"name="menumenu">
<property name="title">
<string>menu</string>
</property>
<addaction name="actionm1_1"/>
<addaction name="separator"/>
<addaction name="actionm1_2"/>
</widget>
<widget class="QMenu"name="menumenu2">
<property name="title">
<string>menu2</string>
</property>
<addaction name="actionm2_1"/>
<addaction name="separator"/>
<addaction name="actionm2_2"/>
</widget>
<addaction name="menumenu"/>
<addaction name="separator"/>
<addaction name="menumenu2"/>
<addaction name="actionmenu3"/>
</widget>
<addaction name="menu"/>
</widget>
<widget class="QStatusBar"name="statusbar"/>
<action name="actionm1_1">
<property name="text">
<string>m1-1</string>
</property>
</action>
<action name="actionm1_2">
<property name="text">
<string>m1-2</string>
</property>
</action>
<action name="actionm2_1">
<property name="text">
<string>m2-1</string>
</property>
</action>
<action name="actionm2_2">
<property name="text">
<string>m2-2</string>
</property>
</action>
<action name="actionmenu3">
<property name="text">
<string>menu3</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>
<ui version="4.0">
<class>Frameclass>
<widget class="QFrame"name="Frame">
<property name="geometry">
<rect>
<x>0x>
<y>0y>
<width>500width>
<height>200height>
rect>
property>
<property name="minimumSize">
<size>
<width>500width>
<height>200height>
size>
property>
<property name="windowTitle">
<string>Framestring>
property>
<layout class="QVBoxLayout"name="verticalLayout">
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Verticalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>20width>
<height>209height>
size>
property>
spacer>
item>
<item>
<layout class="QHBoxLayout"name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>40width>
<height>20height>
size>
property>
spacer>
item>
<item>
<widget class="QPushButton"name="pushButton">
<property name="text">
<string>hellow,click mestring>
property>
widget>
item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>40width>
<height>20height>
size>
property>
spacer>
item>
layout>
item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Verticalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>20width>
<height>209height>
size>
property>
spacer>
item>
layout>
widget>
<resources/>
<connections/>
ui>
<ui version="4.0">
<class>Frameclass>
<widget class="QFrame"name="Frame">
<property name="geometry">
<rect>
<x>0x>
<y>0y>
<width>500width>
<height>200height>
rect>
property>
<property name="minimumSize">
<size>
<width>500width>
<height>200height>
size>
property>
<property name="windowTitle">
<string>Framestring>
property>
<layout class="QVBoxLayout"name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Verticalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>20width>
<height>111height>
size>
property>
spacer>
item>
<item>
<layout class="QHBoxLayout"name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>40width>
<height>20height>
size>
property>
spacer>
item>
<item>
<widget class="QLCDNumber"name="lcdNumber">
<property name="minimumSize">
<size>
<width>100width>
<height>40height>
size>
property>
<property name="maximumSize">
<size>
<width>100width>
<height>40height>
size>
property>
<property name="digitCount">
<number>6number>
property>
<property name="mode">
<enum>QLCDNumber::Decenum>
property>
<property name="segmentStyle">
<enum>QLCDNumber::Flatenum>
property>
<property name="value"stdset="0">
<double>66666.000000000000000double>
property>
<property name="intValue"stdset="0">
<number>66666number>
property>
widget>
item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>40width>
<height>20height>
size>
property>
spacer>
item>
layout>
item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Verticalenum>
property>
<property name="sizeHint"stdset="0">
<size>
<width>20width>
<height>111height>
size>
property>
spacer>
item>
layout>
widget>
<resources/>
<connections/>
ui>
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file'MainWindow.ui'
##
## Created by: Qt User Interface Compiler version 6.4.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import(QCoreApplication,QDate,QDateTime,QLocale,
QMetaObject,QObject,QPoint,QRect,
QSize,QTime,QUrl,Qt)
from PySide6.QtGui import(QAction,QBrush,QColor,QConicalGradient,
QCursor,QFont,QFontDatabase,QGradient,
QIcon,QImage,QKeySequence,QLinearGradient,
QPainter,QPalette,QPixmap,QRadialGradient,
QTransform)
from PySide6.QtWidgets import(QApplication,QMainWindow,QMenu,QMenuBar,
QSizePolicy,QStatusBar,QWidget)
class Ui_MainWindow(object):
def setupUi(self,MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(640,480)
self.actionm1_1 = QAction(MainWindow)
self.actionm1_1.setObjectName(u"actionm1_1")
self.actionm1_2 = QAction(MainWindow)
self.actionm1_2.setObjectName(u"actionm1_2")
self.actionm2_1 = QAction(MainWindow)
self.actionm2_1.setObjectName(u"actionm2_1")
self.actionm2_2 = QAction(MainWindow)
self.actionm2_2.setObjectName(u"actionm2_2")
self.actionmenu3 = QAction(MainWindow)
self.actionmenu3.setObjectName(u"actionmenu3")
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.centralwidget.setEnabled(True)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0,0,640,22))
self.menu = QMenu(self.menubar)
self.menu.setObjectName(u"menu")
self.menumenu = QMenu(self.menu)
self.menumenu.setObjectName(u"menumenu")
self.menumenu2 = QMenu(self.menu)
self.menumenu2.setObjectName(u"menumenu2")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menu.menuAction())
self.menu.addAction(self.menumenu.menuAction())
self.menu.addSeparator()
self.menu.addAction(self.menumenu2.menuAction())
self.menu.addAction(self.actionmenu3)
self.menumenu.addAction(self.actionm1_1)
self.menumenu.addSeparator()
self.menumenu.addAction(self.actionm1_2)
self.menumenu2.addAction(self.actionm2_1)
self.menumenu2.addSeparator()
self.menumenu2.addAction(self.actionm2_2)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self,MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow",u"MainWindow",None))
self.actionm1_1.setText(QCoreApplication.translate("MainWindow",u"m1-1",None))
self.actionm1_2.setText(QCoreApplication.translate("MainWindow",u"m1-2",None))
self.actionm2_1.setText(QCoreApplication.translate("MainWindow",u"m2-1",None))
self.actionm2_2.setText(QCoreApplication.translate("MainWindow",u"m2-2",None))
self.actionmenu3.setText(QCoreApplication.translate("MainWindow",u"menu3",None))
self.menu.setTitle(QCoreApplication.translate("MainWindow",u"\u83dc\u5355\u680f",None))
self.menumenu.setTitle(QCoreApplication.translate("MainWindow",u"menu",None))
self.menumenu2.setTitle(QCoreApplication.translate("MainWindow",u"menu2",None))
# retranslateUi
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file'frame1.ui'
##
## Created by: Qt User Interface Compiler version 6.4.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import(QCoreApplication,QDate,QDateTime,QLocale,
QMetaObject,QObject,QPoint,QRect,
QSize,QTime,QUrl,Qt)
from PySide6.QtGui import(QBrush,QColor,QConicalGradient,QCursor,
QFont,QFontDatabase,QGradient,QIcon,
QImage,QKeySequence,QLinearGradient,QPainter,
QPalette,QPixmap,QRadialGradient,QTransform)
from PySide6.QtWidgets import(QApplication,QFrame,QHBoxLayout,QPushButton,
QSizePolicy,QSpacerItem,QVBoxLayout,QWidget)
class Ui_Frame(object):
def setupUi(self,Frame):
if not Frame.objectName():
Frame.setObjectName(u"Frame")
Frame.resize(500,200)
Frame.setMinimumSize(QSize(500,200))
self.verticalLayout = QVBoxLayout(Frame)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalSpacer_2 = QSpacerItem(20,209,QSizePolicy.Minimum,QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer_2)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.pushButton = QPushButton(Frame)
self.pushButton.setObjectName(u"pushButton")
self.horizontalLayout.addWidget(self.pushButton)
self.horizontalSpacer_2 = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer_2)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalSpacer = QSpacerItem(20,209,QSizePolicy.Minimum,QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
self.retranslateUi(Frame)
QMetaObject.connectSlotsByName(Frame)
# setupUi
def retranslateUi(self,Frame):
Frame.setWindowTitle(QCoreApplication.translate("Frame",u"Frame",None))
self.pushButton.setText(QCoreApplication.translate("Frame",u"hellow,click me",None))
# retranslateUi
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file'frame2.ui'
##
## Created by: Qt User Interface Compiler version 6.4.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide6.QtCore import(QCoreApplication,QDate,QDateTime,QLocale,
QMetaObject,QObject,QPoint,QRect,
QSize,QTime,QUrl,Qt)
from PySide6.QtGui import(QBrush,QColor,QConicalGradient,QCursor,
QFont,QFontDatabase,QGradient,QIcon,
QImage,QKeySequence,QLinearGradient,QPainter,
QPalette,QPixmap,QRadialGradient,QTransform)
from PySide6.QtWidgets import(QApplication,QFrame,QHBoxLayout,QLCDNumber,
QSizePolicy,QSpacerItem,QVBoxLayout,QWidget)
class Ui_Frame(object):
def setupUi(self,Frame):
if not Frame.objectName():
Frame.setObjectName(u"Frame")
Frame.resize(500,200)
Frame.setMinimumSize(QSize(500,200))
self.verticalLayout = QVBoxLayout(Frame)
self.verticalLayout.setObjectName(u"verticalLayout")
self.verticalSpacer = QSpacerItem(20,111,QSizePolicy.Minimum,QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer)
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.horizontalSpacer = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer)
self.lcdNumber = QLCDNumber(Frame)
self.lcdNumber.setObjectName(u"lcdNumber")
self.lcdNumber.setMinimumSize(QSize(100,40))
self.lcdNumber.setMaximumSize(QSize(100,40))
self.lcdNumber.setDigitCount(6)
self.lcdNumber.setMode(QLCDNumber.Dec)
self.lcdNumber.setSegmentStyle(QLCDNumber.Flat)
self.lcdNumber.setProperty("value",66666.000000000000000)
self.lcdNumber.setProperty("intValue",66666)
self.horizontalLayout.addWidget(self.lcdNumber)
self.horizontalSpacer_2 = QSpacerItem(40,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
self.horizontalLayout.addItem(self.horizontalSpacer_2)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalSpacer_2 = QSpacerItem(20,111,QSizePolicy.Minimum,QSizePolicy.Expanding)
self.verticalLayout.addItem(self.verticalSpacer_2)
self.retranslateUi(Frame)
QMetaObject.connectSlotsByName(Frame)
# setupUi
def retranslateUi(self,Frame):
Frame.setWindowTitle(QCoreApplication.translate("Frame",u"Frame",None))
# retranslateUi
# -*- coding: UTF-8 -*-
# File date: Hi_2023/2/22 22:23
# File_name: 03_ui文件转py写法.py
import sys
from PySide6.QtWidgets import QApplication,QMainWindow,QHBoxLayout,QBoxLayout,QFrame,QWidget
from PySide6.QtCore import QFile
from frame1_UI import Ui_Frame as Frame1
from frame2_UI import Ui_Frame as Frame2
from MainWindow_UI import Ui_MainWindow as mainwindow
class frame1(QFrame,Frame1):
def __init__(self,parent=None):
super().__init__(parent)
self.ui = Frame1()
self.setupUi(self)
class frame2(QFrame,Frame2):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)
class MainWindow(QMainWindow,mainwindow):
def __init__(self,parent=None):
super().__init__(parent)
self.setupUi(self)
center = self.centralWidget()
self.setCentralWidget(center)# 设置中心控件
h = QHBoxLayout(center)
h.setDirection(QBoxLayout.Direction.TopToBottom)
self.f1 = frame1()
self.f2 = frame2()
self.f1.pushButton.clicked.connect(lambda: print("hello 靓仔!"))
self.f1.setStyleSheet("background-color:blue;")
self.f2.setStyleSheet("background-color:red;")
h.addWidget(self.f1)
h.addWidget(self.f2)
if __name__ =="__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
这种方式不是很好继承父类,在需要重写一些事件时,不好实现。并且这么写完全没代码提示,不建议使用直接加载UI文件的方法。
from PySide6.QtUiTools import QUiLoader, loadUiType
from PySide6.QtUiTools import loadUiType
generated_class, base_class = loadUiType("themewidget.ui")
# the values will be:
# (, )
widget = base_class()
form = generated_class()
form.setupUi(widget)
# form.a_widget_member.a_method_of_member()
widget.show()
tuple(object, object)
此函数在运行时生成并加载一个.ui文件,并返回一个元组,其中包含对Python类和基类的引用。
我们建议不要使用这种方法,因为工作流应该是从.ui文件生成一个Python文件,然后导入并加载它以使用它,但我们确实知道,当需要这样的功能时,会出现一些情况。
内部进程依赖于位于PATH中的uic。pyside6uic包装使用位于站点包/pyside6/uic中的附带uic,因此如果系统中没有uic,则需要更新PATH以使用该uic。
# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/31 20:43
# File_name: 04-加载UI文件写法-loadUiType加载.py
import sys
from PySide6 import QtWidgets
from PySide6.QtUiTools import QUiLoader,loadUiType
from PySide6.QtWidgets import QHBoxLayout,QBoxLayout,QFrame
formType,baseType = loadUiType('MainWindow.ui')
class myWindow(formType, baseType):
def __init__(self):
super().__init__()
self.loader = QUiLoader()
self.setupUi(self)
self.center = self.centralWidget()# 设置中心控件
self.h = QHBoxLayout(self.center)# 设置布局
self.h.setDirection(QBoxLayout.Direction.TopToBottom)
self.initUi()
def initUi(self):
self.f1: QFrame = self.loader.load("frame1.ui")
self.f2: QFrame = self.loader.load("frame2.ui")
self.f1.setFrameShape(QFrame.Shape.Box)
self.f2.setFrameShape(QFrame.Shape.Box)
self.f1.setStyleSheet("background-color:blue;")
self.f2.setStyleSheet("background-color:red;")
self.f1.pushButton.clicked.connect(lambda: print("hello 靓仔!"))
self.h.addWidget(self.f1)
self.h.addWidget(self.f2)
if __name__ =="__main__":
app = QtWidgets.QApplication(sys.argv)
root = myWindow()
root.show()
app.exit(app.exec())
# -*- coding: UTF-8 -*-
# File date: Hi_2023/4/1 15:50
# File_name: 05-加载UI文件-load加载.py
from PySide6.QtWidgets import QMainWindow,QApplication,QHBoxLayout,QVBoxLayout,QBoxLayout,QFrame,QWidget
from PySide6 import QtWidgets
from PySide6.QtUiTools import QUiLoader,loadUiType
import sys
class myWindow:
def __init__(self):
# 主窗口
self.ui = QUiLoader().load("MainWindow.ui")
self.loader = QUiLoader()
# 设置中心控件
self.center = self.ui.centralWidget()# 设置中心控件
print(type(self.center))
# 设置布局
self.h = QHBoxLayout(self.center)# 设置布局
self.h.setDirection(QBoxLayout.Direction.TopToBottom)
# 初始化ui
self.initUi()
def initUi(self):
self.f1: QFrame = self.loader.load("frame1.ui")
self.f2: QFrame = self.loader.load("frame2.ui")
print(type(self.f1))
self.f1.setStyleSheet("background-color:blue;")
self.f2.setStyleSheet("background-color:red;")
self.f1.pushButton.clicked.connect(lambda: print("hello 靓仔!"))
self.h.addWidget(self.f1)
self.h.addWidget(self.f2)
if __name__ =="__main__":
app = QtWidgets.QApplication(sys.argv)
root = myWindow()
root.ui.show()
app.exit(app.exec())
本文摘自在PyQt中使用qrc/rcc资源系统 - muzing的杂货铺
Qt 资源系统(The Qt Resource System)是一种独立于平台的资源管理器,用于在应用程序的可执行文件中存储二进制文件。对 PyQt 而言,这意味着在 Python 代码中直接以二进制形式存储图标、QSS、长文本翻译等资源文件。使用Qt 资源管理系统可以有效防止资源文件丢失,对于需要打包发布 的 PyQt 程序尤其实用。
在项目中使用Qt 资源系统,大致分为三个步骤:编写 .qrc
文件、使用rcc 编译资源、导入与使用。下文将一一详细讲解。
Qt 资源集合文件(Qt Resource Collection File)一般以 .qrc
作为扩展名保存,故简称 .qrc
文件。其文件格式基于XML,用于将文件系统(硬盘)中的资源文件与 Qt 应用程序关联起来。.qrc
还可以实现为资源分组、设置别名等功能。
下面是一个简单的例子:
Resources
目录下包含图标、关于文档等资源文件。
$ tree Resources
Resources
├── Icons
│ ├── Py2exe-GUI_icon_72px.png
│ └── Python_128px.png
├── Texts
│ └── About_zh.md
└── resources.qrc
在此处新建一个 resources.qrc
文件,内容如下:
DOCTYPE RCC>
<RCC>
<qresource>
<file>Icons/Py2exe-GUI_icon_72px.pngfile>
<file>Icons/Python_icon.icofile>
<file>Texts/About_zh.mdfile>
qresource>
RCC>
注意文件的相对路径是以
.qrc
所在的目录Resources\
为根目录开始计算的。
这样便建立了硬盘上文件系统中原文件与 Qt 资源系统中资源路径之间的联系。
在文件系统中,可以通过目录对不同类型的资源进行分组。在上面的例子中,图标文件都在 Icons/
目录下,而长文本在 Texts/
下。在 .qrc
中,也可以通过指定
标签的 prefix
属性来对资源进行分组:
DOCTYPE RCC>
<RCC>
<qresource prefix="icons">
<file>Icons/Py2exe-GUI_icon_72px.pngfile>
<file>Icons/Python_icon.icofile>
qresource>
<qresource prefix="texts">
<file>Texts/About_zh.mdfile>
qresource>
RCC>
有些资源的文件名很长,每次使用时都输入完整文件名较为繁琐。可以通过在
标签中添加 alias
属性为其创建别名,方便未来在资源路径中使用:
DOCTYPE RCC>
<RCC>
<qresource prefix="icons">
<file alias="Py2exe-GUI_icon">Icons/Py2exe-GUI_icon_72px.pngfile>
<file alias="Python_icon">Icons/Python_icon.icofile>
qresource>
<qresource prefix="texts">
<file alias="About_Text">Texts/About_zh.mdfile>
qresource>
RCC>
Qt 提供了Resource Compiler命令行工具(简称 rcc),用于在构建过程中将资源嵌入 Qt 应用程序。对于 PyQt.也有对应版本的 rcc 工具,用于将 .qrc
中指定的资源文件数据编译至 Python 对象。
当通过 pip
安装 PySide6 或其他 PyQt 时,会同时自动安装对应版本的 rcc 工具。这些工具的调用命令有所不同(详见下表),但使用方式与功能是一致的。激活已安装 PyQt 的 Python 虚拟环境,在命令行(注意不是 Python 交互式解释器)中输入对应的 rcc 命令即可。
平台 | rcc 命令名称 |
---|---|
PySide6 | pyside6-rcc |
PyQt5 | pyrcc5 |
PySide2 | pyside2-rcc |
PyQt6 | 不提供 |
使用PySide6 提供的
pyside6-rcc
工具编译出的.py
文件,也可以放入 PyQt6 项目中使用,只需将文件开头的from PySide6 import QtCore
替换为from PyQt6 import QtCore
即可。
例如,对于 PySide6,在命令行调用命令
pyside6-rcc -o compiled_resources.py resources.qrc
即可将 resources.qrc
中列出的资源文件编译到输出文件 compiled_resources.py
中。
此处以 pyside6-rcc 6.4.1 为例,列出了完整的选项列表(翻译版):
$ pyside6-rcc --help
Usage: /path/to/your/python3/site-packages/PySide6/Qt/libexec/rcc[options]inputs
Qt Resource Compiler version 6.4.1
Options:
-h,--help 显示关于命令行选项的帮助
--help-all 显示包括Qt独有选项在内的所有帮助
-v,--version 显示版本信息
-o,--output <file> 将输出写入到 <file> 中,而不是 stdout 中
-t,--temp <file> 为大资源文件使用临时文件 <file>
--name <name> 用<name> 创建一个外部初始化函数
--root <path> 用根目录 <path> 作为资源访问路径的前缀
--compress-algo <algo> 使用<algo> 算法压缩输入文件([zlib],none)
--compress <level> 按 <level> 级别压缩输入文件
--no-compress 禁用所有压缩,等同于 --compress-algo=none
--no-zstd 禁止使用zstd 压缩
--threshold <level> 衡量是否值得进行压缩的阈值
--binary 输出一个作为动态资源使用的二进制文件
-g,--generator <cpp|python|python2> 选择生成器
--pass <number> Pass number for big resources
--namespace 关闭命名空间宏
--verbose 启用verbose 模式
--list 只列出 .qrc 文件条目,不生成代码
--list-mapping 只输出 .qrc 中定义的资源路径与文件系统路径的映射,不生成代码
-d,--depfile <file> 向 <file> 中写入一个包含 .qrc 依赖项的 depfile
--project 输出一个包含当前目录下所有文件的资源文件
--format-version <number> 写入 RCC 格式的版本
Arguments:
inputs 输入文件(*.qrc)
运行成功后,在 .qrc
中声明的所有资源文件都已经被编译到 compiled_resources.py
这个 Python 文件中,不妨打开查看其内容:
# 以下为 compiled_resources.py 文件中内容
# Resource object code(Python 3)
# Created by: object code
# Created by: The Resource Compiler for Qt version 6.4.1
# WARNING! All changes made in this file will be lost!
from PySide6 import QtCore
qt_resource_data = b"......"
qt_resource_name = b"......"
qt_resource_struct = b"......"
def qInitResources():
QtCore.qRegisterResourceData(0x03,qt_resource_struct,qt_resource_name,qt_resource_data)
def qCleanupResources():
QtCore.qUnregisterResourceData(0x03,qt_resource_struct,qt_resource_name,qt_resource_data)
qInitResources()
最上方的注释标明了该文件由与 Qt6.4.1 版本匹配的资源编译器生成。并警告用户不要直接编辑该文件,因为所有修改都会被下一次编译操作覆盖掉。
接下来是三段长长的二进制编码字符串,其中正是资源文件:
qt_resource_data
- 资源文件内容数据qt_resource_name
- 资源文件名称qt_resource_struct
- 资源结构还有两个函数 qInitResources()
与 qCleanupResources()
,分别对应向 Qt 中注册资源与清理资源。
代码的最后一行调用了注册资源函数。
对于 PyQt 程序,从「直接加载使用资源文件」切换到「使用Qt 资源系统读取资源」,还需要如下步骤:
1.在主程序中导入编译后的资源
2.用「资源路径」替换「文件路径」
3.由使用Python 内置 open()
函数改为使用Qt 中QFile类或QDir类提供的 open()
方法
在主程序中添加 import 导入语句,将刚才获得的 compiled_resources.py
导入:
import compiled_resources # type: ignore
因为 import 的过程会执行该模块中的所有代码,也就自动调用了 qInitResources()
函数,完成了资源的注册与加载。
PyCharm 等 IDE 可能将此行代码判断为"未使用的 import 语句"而提示一个弱警告。可以通过在该行末尾添加特殊的
# type: ignore
注释来显式告知静态检查器忽略此行,消除这种不必要的警告。
对于直接使用资源原文件,会使用其在文件系统中的路径,例如
icon = QPixmap("Icons/Python_icon.ico")
而在 Qt 资源系统中使用,则需要将文件路径替换为「资源路径」。资源路径由 .qrc
文件决定。
对于最一般的情况,直接在文件名前添加 :/
即可得到其资源路径:
<qresource>
<file>Icons/Python_icon.icofile>
qresource>
icon = QPixmap(":/Python_icon.ico")
对于有前缀进行分组的,则需要在文件名前添加 :/$prefix$/
作为资源路径:
<qresource prefix="icons">
<file>Icons/Python_icon.icofile>
qresource>
icon = QPixmap(":/icons/Python_icon.ico")
对于指定了别名的,可以直接使用别名:
<qresource prefix="icons">
<file alias="Py_ico">Icons/Python_icon.icofile>
qresource>
icon = QPixmap(":/icons/Py_ico")
需要使用Qt 提供的QFile或QDir读取编译后的资源文件,而不再能使用Python 提供的 open()
函数等。
例如,一段从 Markdown 文件中读取应用程序"关于"文本的代码,使用直接读取原资源文件的写法如下:
def get_about_text():
about_file = open(../../Resources/About.md","r",encoding="utf-8")# 调用Python内置的open()
about_text = about_file.read()
about_file.close()
return about_text
而使用Qt 资源系统后,需要修改为如下形式:
from PySide6.QtCore import QFile,QIODevice
def get_about_text():
# 使用Qt风格读取文本文件
about_file = QFile(":/texts/About_Text")# 使用Qt中的QFile类
about_file.open(QIODevice.ReadOnly | QIODevice.Text)# 打开文件
about_text = str(about_file.readAll(),encoding="utf-8")# 读取文件,并将 QBtyeArray 转为 str
about_file.close()
return about_text
对于图片,可以在创建QIcon、QImage、QPixmap对象时将直接资源路径作为参数传入:
icon = QPixmap(":/icons/Python_icon.ico")
不加别名时,文件引用方式是:/文件相对于qrc文件路径
,例如
<file >images/cut.pngfile>
引用方式是 :/images/cut.png
加前缀时,文件引用方式是:/前缀/文件相对于qrc文件路径
<file prefix="/icon">images/cut.pngfile>
引用::/icon/images/cut.png
加别名和前缀时是:/前缀/文件别名
。 注意
:的是使用别名后不支持再使用路径来引用了
<qresource prefix="/icon">
<file alias="cut-img.png">images/cut.png</file>
</qresource>
引用::/icon/cut.png
当qrc引用父级或更高层目录且未设置别名时,默认会在qrc文件中自动使用相对路径如下:
<RCC>
<qresource prefix="icon">
<file alias="i1">../../Resources/Icons/arrow_48px.ico</file>
<file alias="i2">../../Resources/Icons/arrow_down_48px.ico</file>
<file alias="i3">../../Resources/Icons/arrow_left_48px.ico</file>
<file>../../Resources/Icons/arrow_right_48px.ico</file>
</qresource>
</RCC>
当需要引用第四张图时可这么引用:QPixmap(":/icon/Resources/Icons/arrow_right_48px.ico
直接忽略前面..
即可,也可以通过qrc目录层级来确定直接引用方式。
可以将 rcc 工具添加至 PyCharm 中,避免每次使用都需要输入繁琐的命令行。(目前版本的 PyCharm 中已经原生内置了对 pyrcc4
和 pyside-rcc
的支持,但其他版本的 rcc 工具快捷使用仍需自行在「外部工具」中创建。)
打开 文件 -> 设置 -> 工具 -> 外部工具(File -> Settings -> Tools -> External Tools)
然后创建工具。配置可以参考下图,仍然以 pyside6-rcc
为例:
其中比较重要的是「工具设置」里面的三行配置:
程序: $PyInterpreterDirectory$/pyside6-rcc
实参: -o compiled_$FileNameWithoutExtension$.py $FileName$
工作目录: $FileDir$
其中凡是以一对 $
包裹的字符,均为 PyCharm 提供的宏,分别代表「Python 解释器目录」、「不带扩展名的文件名」、「文件名」和「文件所在目录」。
完成配置后,在待编译的 .qrc
文件上打开右键菜单,找到「外部工具 - pyside6-rcc」,点击即可运行,非常方便。
安装Qt for Python插件后,VS Code 编辑器会对 .qrc
文件提供一定的语法高亮、亦可一键编译。
# VS Code 插件安装命令:
ext install seanwu.vscode-qt-for-python
此小节参考自 https://blog.csdn.net/anbuqi/article/details/120455219。
直接在文本编辑器中以 XML 形式编写复杂的 .qrc
文件时,较为繁琐、易出错,可以考虑安装 Qt 官方的QtCreaterIDE 来生成 .qrc
文件。
$ tree resource
resource
├── icon
│ ├── ic_last_step.svg
│ ├── ic_next_step.svg
│ └── ic_start.svg
└── res.qrc
首先启动 QtCreater,在 resource/
目录中新建 res.qrc
文件:
然后以 icon 前缀创建一个分组:
接着用Add Files 按钮把图标文件添加进来,并保存:
此时得到了 res.qrc
:
<RCC>
<qresource prefix="/icon">
<file>icon/ic_last_step.svgfile>
<file>icon/ic_next_step.svgfile>
<file>icon/ic_start.svgfile>
qresource>
RCC>
为了缩短引用路径,还可以为每个图标文件设置别名(alias):
现在得到这样的 res.qrc
:
<RCC>
<qresource prefix="/icon">
<file alias="ic_last_step">icon/ic_last_step.svgfile>
<file alias="ic_next_step">icon/ic_next_step.svgfile>
<file alias="ic_start">icon/ic_start.svgfile>
qresource>
RCC>
<RCC>
<qresource prefix="icon">
<file alias="i1">../../Resources/Icons/arrow_48px.ico</file>
<file alias="i2">../../Resources/Icons/arrow_down_48px.ico</file>
<file alias="i3">../../Resources/Icons/arrow_left_48px.ico</file>
<file>../../Resources/Icons/arrow_right_48px.ico</file>
</qresource>
<qresource prefix="image">
<file alias="p1">../../Resources/Images/left.png</file>
<file alias="p2">../../Resources/Images/left2.png</file>
<file alias="p3">../../Resources/Images/left3.png</file>
<file>../../Resources/Images/leftdown.png</file>
</qresource>
</RCC>
# -*- coding: UTF-8 -*-
# File date: Hi_2023/4/1 20:37
# File_name: 06-使用qrc文件.py
import sys
from PySide6.QtGui import QPixmap
from PySide6.QtWidgets import QWidget,QApplication,QLabel,QBoxLayout,QHBoxLayout,QVBoxLayout
import image_rc # type: ignore
class MyWindow(QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.set_windows()
self.demo()
def set_windows(self):
self.resize(300,300)# 设置窗口大小
self.setWindowTitle("hello world")# 设置窗口标题
def demo(self):
win_lay = QVBoxLayout(self)
ico_h = QHBoxLayout()
img_h = QHBoxLayout()
win_lay.addLayout(ico_h)
win_lay.addLayout(img_h)
icon1 = QLabel()
icon2 = QLabel()
icon3 = QLabel()
image_1 = QLabel()
image_2 = QLabel()
image_3 = QLabel()
icon1.setPixmap(QPixmap(":/icon/i1"))
icon2.setPixmap(QPixmap(":/icon/i2"))
icon3.setPixmap(QPixmap(":/icon/Resources/Icons/arrow_right_48px.ico"))
image_1.setPixmap(QPixmap(":/image/p1"))
image_2.setPixmap(QPixmap(":/image/p2"))
image_3.setPixmap(QPixmap(":/image/p3"))
ico_h.addWidget(icon1)
ico_h.addWidget(icon2)
ico_h.addWidget(icon3)
img_h.addWidget(image_1)
img_h.addWidget(image_2)
img_h.addWidget(image_3)
if __name__ =='__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())
有时应用程序需要在不同的语言环境下使用不同的文件,可以通过设置 lang
属性来轻松实现。下面是一个例子:
<qresource>
<file>cut.jpgfile>
qresource>
<qresource lang="fr">
<file alias="cut.jpg">cut_fr.jpgfile>
qresource>
当系统语言为其他语言时,Qt 程序将会使用文件 cut.jpg
;而当系统语言为法语时,会自动替换为 cut_fr.jpg
。
rcc 会尝试压缩内容以优化硬盘空间使用。默认情况下,它将进行启发式检查以确认是否值得压缩。如果不能充分压缩,则将直接存储非压缩的内容。可以使用-threshold
选项控制此判断的阈值。例如,默认情况下阈值为 70,表示只有在压缩后的文件比原文件小 70%(不超过原文件大小的 30%)时才有必要压缩。
rcc -threshold 25 myresources.qrc
在某些情况下也可以关闭压缩功能。一种常见情况是,某些资源已经是压缩后的格式(例如 .png
文件),再次压缩几乎不会进一步减小文件体积,但会占用CPU 成本。另一种情况是,硬盘空间非常充裕,期望应用程序在运行时可以将内容存储在干净的内存页中。在命令行中添加 -no-compress
命令以关闭压缩。
rcc -no-compress myresources.qrc
还可以控制 rcc 使用的压缩算法与压缩级别,例如:
rcc -compress 2 -compress-algo zlib myresources.qrc
表示使用zlib 压缩算法,压缩等级为 2。
除了在命令行调用rcc 时指定选项,还可以在 .qrc
文件中控制阈值、压缩算法与压缩等级:
<qresource>
<file compress="1"compress-algo="zstd">data.txtfile>
qresource>
rcc 具体支持的压缩算法类型与压缩等级,参见官方文档。
1.Qt6 官方文档:The Qt Resource System
2.Qt6 官方文档:Resource Compiler(rcc)
3.Qt for Python 官方教程:Using .qrc Files(pyside6-rcc)
4.pyside6(1):Qt 资源系统和qrc文件使用
5.Stack Overflow: How can resources be provided in PyQt6(which has no pyrcc)?
对于可视化编程,需要将界面上的控件有机结合起来,实现控件功能的联动和交互操作。
通过信号(signal)与槽(slot)机制实现的交互功能。
信号与槽是PySide6编程的基础,也是Qt的一大创新,有了信号与槽的编程机制,在PySide6中处理界面上各个控件的交互操作时变得更加直观和简单。
信号是指从QObject类继承的控件(窗口、按钮、文本框、列表框等)在某个动作下或状态发生改变时发出的一个指令或一个信息
例如一个按钮被单击(clicked)、右击一个窗口(customContextMenuRequested)、一个输入框中文字的改变(textChanged)等
当这些控件的状态发生变化或者外界对控件进行输人时,让这些控件发出一个信息,来通知系统其某种状态发生了变化或者得到了外界的输入,以便让系统对外界的输人进行响应。
槽是系统对控件发出的信号进行的响应,或者产生的动作,通常用函数来定义系统的响应或动作。
例如对于单击"计算"按钮,按钮发出被单击的信号,然后编写对应的函数,当控件发出信号时,就会自动执行与信号关联的函数。
信号与槽的关系可以是一对一,也可以是多对多,即一个信号可以关联多个槽函数,一个槽函数也可以接收多个信号。PySide6已经为控件编写了一些信号和槽函数,使用前需要将信号和槽函数进行连接,另外用户还可以自定义信号和自定义槽函数。
查询控件的信号时,会发现有些控件有多个名字相同但是参数不同的信号。
例如对于按钮有clicked()和 clicked(bool)两种信号,一种不需要传递参数的信号,另一种传递布尔型参数的信号。
这种信号名称相同、参数不同的信号称为重载(overload)型信号。
对于重载型信号定义自动关联槽函数时,需要在槽函数前加修饰符@slot(type)
声明是对哪个信号定义槽函数,其中type
是信号传递的参数类型。
例如如果对按钮的clicked(bool)信号定义自动关联槽函数,需要在槽函数前加入@slot(bool)
进行修饰;如果对按钮的clicked()信号定义自动关联槽函数,需要在槽函数前加人@slot()进行修饰。
需要注意的是,在使用@slot(type)
修饰符前,应提前用from PySide6.QtCore import slot
语句导人槽函数。
定义一个信号后就有连接connect()
、发送emit()
、断开disconnect()
属性
已连接的信号非重载类型的直接使用signaName.disconnect()
断开连接即可
重载类型的使用signaName[type].disconnect()
断开连接
除了使用控件内置信号定义自动连接的槽函数外,还可以将控件内置信号手动连接到其他函数上,这时需要用到信号的 connect()
方法。
btnCalculate.clicked.connect(self.method)
语句将按钮的单击信号clicked
与method()
函数进行连接
也可以在主程序中,在消息循环语句前用myWindow.ui.btnCalculate.clicked.connect(myWindow.method)
语句进行消息与槽函数的连接