QT for Python 6.1.2

写在前面

pyside6 遵循 LGPL / GPL 的开源协议。官网

本笔记只为记录本人的学习状态,不做其他用途。

安装/更新/VSCode插件

安装:python -m pip install pyside6
更新:python -m pip install --upgrade pyside6
安装 Visual Studio Code 插件:(Ctrl+P)-> ext install seanwu.vscode-qt-for-python

窗口

基本窗口

import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtWidgets import QLabel

class DemoWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.label = QLabel("Hello World", alignment=QtCore.Qt.AlignCenter)

        self.layout = QtWidgets.QVBoxLayout(self) # 窗口布局
        self.layout.addWidget(self.label) # 控件绑定到布局中

        self.setLayout(self.layout)

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    widget = DemoWidget()
    widget.resize(200, 150)
    widget.show()
    sys.exit(app.exec())

对话框

import sys
from PySide6 import QtWidgets

class DemoDialog(QtWidgets.QDialog):

    def __init__(self, parent=None):
        super(DemoDialog, self).__init__(parent)
        self.setWindowTitle("对话框")

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dialog = DemoDialog()
    dialog.show()
    sys.exit(app.exec())

布局

垂直布局

from PySide6 import QtWidgets

layout = QtWidgets.QVBoxLayout(self) # 窗口布局

# 顺序添加
layout.addWidget(obj1)
layout.addWidget(obj2)

# 比例添加( obj1 和 obj2 将以 1:2 的比例,划分所有的垂直区域)
layout.addWidget(obj1, 1)
layout.addWidget(obj2, 2)

'''最后记得绑定到主窗口

self 表示 继承 QWidget 的类对象
'''
self.setLayout(layout)

水平布局

from PySide6 import QtWidgets

layout = QtWidgets.QHBoxLayout()

# 顺序添加
layout.addWidget(obj1)
layout.addWidget(obj2)

# 比例添加( obj1 和 obj2 将以 1:2 的比例,划分所有的水平区域)
layout.addWidget(obj1, 1)
layout.addWidget(obj2, 2)

'''最后记得绑定到主窗口

self 表示 继承 QWidget 的类对象
'''
self.setLayout(layout)

布局区域

widget = QtWidgets.QWidget()
widget.setLayout(layout)

'''用此方法可以将一个布局添加进另一个布局中

即:
    layout2 = QtWidgets.QHBoxLayout()
    layout2.addWidget(widget)
'''

基本控件

标签

from PySide6.QtWidgets import QLabel
from PySide6 import QtCore

# 简单文本
label = QLabel("Hello World")

# 对齐方式
label = QLabel("Hello World", alignment=QtCore.Qt.AlignCenter)

# HTML文本
label = QLabel("<font style='color: red;'>红色</font>")

输入框

from PySide6.QtWidgets import QLineEdit

edit = QLineEdit("输入...")

# 取输入框内的文本
edit_text = edit.text()

按钮

import sys
from PySide6 import QtCore, QtWidgets
from PySide6.QtWidgets import QPushButton

class DemoWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        self.button = QPushButton('按钮')

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(self.button)

        self.button.clicked.connect(self.hello)

    @QtCore.Slot()
    def hello(self):
        print("Hello World!")

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    widget = DemoWidget()
    widget.resize(200, 150)
    widget.show()
    sys.exit(app.exec())

列表控件

普通列表

from PySide6 import QtCore, QtWidgets

list_widget = QtWidgets.QListWidget()
for i in range(10):
    item = QtWidgets.QListWidgetItem(f"Item {i}")
    item.setTextAlignment(QtCore.Qt.AlignCenter)
    list_widget.addItem(item)

表格控件

普通表格

import sys
from PySide6 import QtWidgets
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QTableWidget, QTableWidgetItem

colors = [
    ("Red", "#FF0000"),
    ("Green", "#00FF00"),
    ("Blue", "#0000FF"),
    ("Black", "#000000"),
    ("White", "#FFFFFF"),
    ("Electric Green", "#41CD52"),
    ("Dark Blue", "#222840"),
    ("Yellow", "#F9E56d"),
]

class DemoDialog(QtWidgets.QDialog):

    def __init__(self, parent=None):
        super(DemoDialog, self).__init__(parent)
        self.setWindowTitle("表格")

        table = QTableWidget()
        table.setRowCount(len(colors))
        table.setColumnCount(len(colors[0]) + 1)
        table.setHorizontalHeaderLabels(["Name", "Hex Code", "Color"])

        for i, (name, code) in enumerate(colors):
            item_name = QTableWidgetItem(name)
            item_code = QTableWidgetItem(code)
            item_color = QTableWidgetItem()
            item_color.setBackground(self.get_rgb_from_hex(code))
            table.setItem(i, 0, item_name)
            table.setItem(i, 1, item_code)
            table.setItem(i, 2, item_color)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(table)

    def get_rgb_from_hex(self, code):
        code_hex = code.replace("#", "")
        rgb = tuple(int(code_hex[i:i+2], 16) for i in (0, 2, 4))
        return QColor.fromRgb(rgb[0], rgb[1], rgb[2])

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dialog = DemoDialog()
    dialog.show()
    sys.exit(app.exec())

树控件

基本树

import sys
from PySide6 import QtWidgets
from PySide6.QtWidgets import QTreeWidget, QTreeWidgetItem

data = {
    "Project A": ["file_a.py", "file_a.txt", "something.xls"],
    "Project B": ["file_b.csv", "photo.jpg"],
    "Project C": [],
}

class DemoDialog(QtWidgets.QDialog):

    def __init__(self, parent=None):
        super(DemoDialog, self).__init__(parent)
        self.setWindowTitle("树")

        tree = QTreeWidget()
        tree.setColumnCount(2)
        tree.setHeaderLabels(["Name", "Type"])

        items = []
        for key, values in data.items():
            item = QTreeWidgetItem([key])
            for value in values:
                ext = value.split(".")[-1].upper()
                child = QTreeWidgetItem([value, ext])
                item.addChild(child)
            items.append(item)
        tree.insertTopLevelItems(0, items)

        self.layout = QtWidgets.QVBoxLayout(self)
        self.layout.addWidget(tree)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    dialog = DemoDialog()
    dialog.show()
    sys.exit(app.exec())

.ui文件

UI设计器

安装: python -m pip install pyside6

命令行运行:pyside6-designer

间接运行

<?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>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>110</x>
      <y>80</y>
      <width>201</width>
      <height>81</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>400</width>
     <height>20</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

假设上述 XML 命名为 main.ui

那么将其转换为py文件:pyside6-uic main.ui > main.py,输出到 main.py 文件中。默认 生成的类名为 Ui_MainWindow

因此,代码中引入:from main import Ui_MainWindow,完整代码:

import sys
from PySide6.QtWidgets import QApplication, QMainWindow
from main import Ui_MainWindow

class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec())

报错:ValueError: source code string cannot contain null bytes,将编码改为 utf-8 即可。

直接运行

import sys
from PySide6.QtUiTools import QUiLoader
from PySide6.QtWidgets import QApplication
from PySide6.QtCore import QFile, QIODevice

app = QApplication(sys.argv)

ui_file = QFile("main.ui")
if not ui_file.open(QIODevice.ReadOnly):
    print(f"Cannot open main.ui: {ui_file.errorString()}")
    sys.exit(-1)

loader = QUiLoader()
window = loader.load(ui_file)
ui_file.close()

if not window:
    print(loader.errorString())
    sys.exit(-1)

window.show()
sys.exit(app.exec())

.qml文件

基本

import QtQuick 2.0

Rectangle {
    width: 200
    height: 200
    color: "green"

    Text {
        text: "Hello World"
        anchors.centerIn: parent
    }
}
代码中引入,并运行

from PySide6.QtWidgets import QApplication
from PySide6.QtQuick import QQuickView
from PySide6.QtCore import QUrl

app = QApplication([])
view = QQuickView()
url = QUrl("view.qml")

view.setSource(url)
view.setResizeMode(QQuickView.SizeRootObjectToView)
view.show()
app.exec()

官方示例

import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import QtQuick.Controls.Material 2.12

ApplicationWindow {
    id: page
    width: 800
    height: 400
    visible: true

    GridLayout {
        id: grid
        columns: 2
        rows: 3

        ColumnLayout {
            spacing: 2
            Layout.preferredWidth: 400

            Text {
                id: leftlabel
                Layout.alignment: Qt.AlignHCenter
                color: "white"
                font.pointSize: 16
                text: "Qt for Python"
                Layout.preferredHeight: 100
                Material.accent: Material.Green
            }

            RadioButton {
                id: italic
                text: "Italic"
                onToggled: {
                    leftlabel.font.italic = con.getItalic(italic.text)
                    leftlabel.font.bold = con.getBold(italic.text)
                    leftlabel.font.underline = con.getUnderline(italic.text)

                }
            }
            RadioButton {
                id: bold
                text: "Bold"
                onToggled: {
                    leftlabel.font.italic = con.getItalic(bold.text)
                    leftlabel.font.bold = con.getBold(bold.text)
                    leftlabel.font.underline = con.getUnderline(bold.text)
                }
            }
            RadioButton {
                id: underline
                text: "Underline"
                onToggled: {
                    leftlabel.font.italic = con.getItalic(underline.text)
                    leftlabel.font.bold = con.getBold(underline.text)
                    leftlabel.font.underline = con.getUnderline(underline.text)
                }
            }
            RadioButton {
                id: noneradio
                text: "None"
                checked: true
                onToggled: {
                    leftlabel.font.italic = con.getItalic(noneradio.text)
                    leftlabel.font.bold = con.getBold(noneradio.text)
                    leftlabel.font.underline = con.getUnderline(noneradio.text)
                }
            }
        }

        ColumnLayout {
            id: rightcolumn
            spacing: 2
            Layout.columnSpan: 1
            Layout.preferredWidth: 400
            Layout.preferredHeight: 400
            Layout.fillWidth: true

            RowLayout {
                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter


                Button {
                    id: red
                    text: "Red"
                    highlighted: true
                    Material.accent: Material.Red
                    onClicked: {
                        leftlabel.color = con.getColor(red.text)
                    }
                }
                Button {
                    id: green
                    text: "Green"
                    highlighted: true
                    Material.accent: Material.Green
                    onClicked: {
                        leftlabel.color = con.getColor(green.text)
                    }
                }
                Button {
                    id: blue
                    text: "Blue"
                    highlighted: true
                    Material.accent: Material.Blue
                    onClicked: {
                        leftlabel.color = con.getColor(blue.text)
                    }
                }
                Button {
                    id: nonebutton
                    text: "None"
                    highlighted: true
                    Material.accent: Material.BlueGrey
                    onClicked: {
                        leftlabel.color = con.getColor(nonebutton.text)
                    }
                }
            }
            RowLayout {
                Layout.fillWidth: true
                Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
                Text {
                    id: rightlabel
                    color: "white"
                    text: "Font size"
                    Material.accent: Material.White
                }
                Slider {
                    width: rightcolumn.width*0.6
                    Layout.alignment: Qt.AlignRight
                    id: slider
                    value: 0.5
                    onValueChanged: {
                        leftlabel.font.pointSize = con.getSize(value)
                    }
                }
            }
        }
    }
}
Python 代码
import sys
import os
from pathlib import Path

from PySide6.QtCore import QObject, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

class Bridge(QObject):

    @Slot(str, result=str)
    def getColor(self, color_name):
        if color_name.lower() == "red":
            return "#ef9a9a"
        elif color_name.lower() == "green":
            return "#a5d6a7"
        elif color_name.lower() == "blue":
            return "#90caf9"
        else:
            return "white"

    @Slot(float, result=int)
    def getSize(self, s):
        size = int(s * 42) # Maximum font size
        if size <= 0:
            return 1
        else:
            return size

    @Slot(str, result=bool)
    def getItalic(self, s):
        if s.lower() == "italic":
            return True
        else:
            return False

    @Slot(str, result=bool)
    def getBold(self, s):
        if s.lower() == "bold":
            return True
        else:
            return False

    @Slot(str, result=bool)
    def getUnderline(self, s):
        if s.lower() == "underline":
            return True
        else:
            return False


if __name__ == '__main__':
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # Instance of the Python object
    bridge = Bridge()

    # Expose the Python object to QML
    context = engine.rootContext()
    context.setContextProperty("con", bridge)

    # Get the path of the current directory, and then add the name
    # of the QML file, to load it.
    qmlFile = Path(__file__).parent / 'view.qml'
    engine.load(os.fspath(qmlFile.resolve()))

    if not engine.rootObjects():
        sys.exit(-1)

    sys.exit(app.exec())

样式

官方参考

官方参考

直接绑定

ctrl.setStyleSheet("""
    background-color: #000000;
    color: #FFFFFF;
""")

间接绑定

新建一个样式文件:style.qss (类似于 CSS)

编辑样式文件:

QLabel#test {
    color: red;
}
#test 来自于控件 .setObjectName('test') 设定的名称 test。如果不指定,直接写为 QLabel {color: red;} 将作用于所有的 QLabel 实体。

代码中导入样式文件:

...

app = QApplication()

with open("style.qss", "r") as f:
    _style = f.read()
    app.setStyleSheet(_style)

...

自定义

自定义字体/图标

官方教程

颜色

from PySide6.QtGui import QColor

# '#F25A5B
QColor.fromRgb('F2', '5A', '5B')

'''适用于 setBackground() 等方法
'''

对齐方式

from PySide6 import QtCore

'''参数设置

其它的类比即可
'''
QLabel("Hello World", alignment=QtCore.Qt.AlignCenter)



'''属性设置

具体有没有下面的属性,需要结合官方文档查看
'''
# 设置文本居中对齐
ctrl.setTextAlignment(QtCore.Qt.AlignCenter)

官方示例代码

前往链接

总览

模块名 应用场景 注意点
QtWidgets 窗口小部件,用于添加按钮、标签等控件 在不同平台有不同的外观