Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PySide6 GUI 编程(45):QRunnable小练习之进度条控制

PySide6 GUI 编程(45):QRunnable小练习之进度条控制

原创
作者头像
bowenerchen
修改于 2024-09-17 00:40:09
修改于 2024-09-17 00:40:09
34203
代码可运行
举报
文章被收录于专栏:编码视界编码视界
运行总次数:3
代码可运行

简单的进度条功能

示例代码

代码语言:python
代码运行次数:2
运行
AI代码解释
复制
from __future__ import annotations

import sys
import threading
import time
from datetime import datetime
from random import randint

from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QProgressBar, QPushButton, QVBoxLayout, QWidget


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class WorkerSignals(QObject):
    begin = Signal(str, str)
    finished = Signal(str, str)
    progress_value = Signal(int)  # 进度值


class MyWorker(QRunnable):
    def __init__(self, *args, **kwargs):
        # 传递参数给 Worker
        super().__init__()
        print('args=', args, 'kwargs=', kwargs)
        # 初始化信号和槽
        self.signals = WorkerSignals()
        self.args = args
        self.kwargs = kwargs

    @Slot()
    def run(self) -> None:
        thread_id = str(threading.get_ident())
        # 开始工作
        self.signals.begin.emit(thread_id, get_time_str())
        total_progress = 1000
        for i in range(total_progress):
            time.sleep(float(randint(100, 500)) / 1000)  # 模拟耗时操作
            self.signals.progress_value.emit(i)  # 发送信号,更新进度值
        # 结束工作
        self.signals.finished.emit(thread_id, get_time_str())


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 1000)
        self.label = QLabel('0.0%')
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.start_threads)

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.progress_bar)
        v_layout.addWidget(self.label)
        v_layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(v_layout)

        self.setCentralWidget(container)

        self.threads = QThreadPool()
        print('maximum threads: {}'.format(self.threads.maxThreadCount()))

    def update_progress_value(self, value: int) -> None:
        percentage = (value / 1000) * 100  # 计算百分比
        self.label.setText(f'{percentage:.1f}%')
        self.progress_bar.setValue(value)

    def handle_worker_begin(self, thread_id: str, time_str: str) -> None:
        print('BEGIN thread_id=', thread_id, ', time_str =', time_str)

    def handle_worker_finished(self, thread_id: str, time_str: str) -> None:
        print('FINISHED thread_id=', thread_id, ', time_str =', time_str)

    def start_threads(self) -> None:
        worker = MyWorker()
        worker.signals.begin.connect(self.handle_worker_begin)
        worker.signals.finished.connect(self.handle_worker_finished)
        worker.signals.progress_value.connect(self.update_progress_value)
        self.threads.start(worker)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

运行效果

期望的效果

期望的正常运行效果
期望的正常运行效果

随之而来的问题

覆盖写问题
多次点击 start 导致的覆盖写问题
多次点击 start 导致的覆盖写问题
窗口关闭问题
窗口关闭带来的 panic 问题
窗口关闭带来的 panic 问题
异常 panic
异常 panic

优化一:只允许开启一个进度条实例

示例代码

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
from __future__ import annotations

import sys
import threading
import time
from datetime import datetime
from random import randint

from PySide6.QtCore import QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QProgressBar, QPushButton, QVBoxLayout, QWidget


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class WorkerSignals(QObject):
    begin = Signal(str, str)
    finished = Signal(str, str)
    progress_value = Signal(int)  # 进度值


class MyWorker(QRunnable):
    def __init__(self, *args, **kwargs):
        # 传递参数给 Worker
        super().__init__()
        print('args=', args, 'kwargs=', kwargs)
        # 初始化信号和槽
        self.signals = WorkerSignals()
        self.args = args
        self.kwargs = kwargs

    @Slot()
    def run(self) -> None:
        thread_id = str(threading.get_ident())
        # 开始工作
        self.signals.begin.emit(thread_id, get_time_str())
        total_progress = 1000
        for i in range(total_progress):
            time.sleep(float(randint(100, 500)) / 1000)  # 模拟耗时操作
            self.signals.progress_value.emit(i)  # 发送信号,更新进度值
        # 结束工作
        self.signals.finished.emit(thread_id, get_time_str())


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 1000)
        self.label = QLabel('0.0%')
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.start_threads)

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.progress_bar)
        v_layout.addWidget(self.label)
        v_layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(v_layout)

        self.setCentralWidget(container)

        self.threads = QThreadPool()
        print('maximum threads: {}'.format(self.threads.maxThreadCount()))

    def update_progress_value(self, value: int) -> None:
        percentage = (value / 1000) * 100  # 计算百分比
        self.label.setText(f'{percentage:.1f}%')
        self.progress_bar.setValue(value)

    def handle_worker_begin(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(False) # 线程开始后将按钮设置为禁用状态
        print('BEGIN thread_id=', thread_id, ', time_str =', time_str)

    def handle_worker_finished(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(True) # 线程完成后将按钮设置为可用状态
        print('FINISHED thread_id=', thread_id, ', time_str =', time_str)

    def start_threads(self) -> None:
        worker = MyWorker()
        worker.signals.begin.connect(self.handle_worker_begin)
        worker.signals.finished.connect(self.handle_worker_finished)
        worker.signals.progress_value.connect(self.update_progress_value)
        self.threads.start(worker)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

示例效果

只允许开启一个进度条实例
只允许开启一个进度条实例

优化二:主窗口关闭检测

示例代码

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
from __future__ import annotations

import sys
import threading
import time
from datetime import datetime
from random import randint

from PySide6.QtCore import QEvent, QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QMessageBox, QProgressBar, QPushButton, QVBoxLayout, QWidget


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class WorkerSignals(QObject):
    begin = Signal(str, str)
    finished = Signal(str, str)
    progress_value = Signal(int)  # 进度值


class MyWorker(QRunnable):
    def __init__(self, *args, **kwargs):
        # 传递参数给 Worker
        super().__init__()
        print('args=', args, 'kwargs=', kwargs)
        # 初始化信号和槽
        self.signals = WorkerSignals()
        self.args = args
        self.kwargs = kwargs

    @Slot()
    def run(self) -> None:
        thread_id = str(threading.get_ident())
        # 开始工作
        self.signals.begin.emit(thread_id, get_time_str())
        total_progress = 1000
        for i in range(total_progress):
            time.sleep(float(randint(100, 500)) / 1000)  # 模拟耗时操作
            self.signals.progress_value.emit(i)  # 发送信号,更新进度值
        # 结束工作
        self.signals.finished.emit(thread_id, get_time_str())


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.active_workers = 0
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 1000)
        self.label = QLabel('0.0%')
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.start_threads)

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.progress_bar)
        v_layout.addWidget(self.label)
        v_layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(v_layout)

        self.setCentralWidget(container)

        self.threads = QThreadPool()
        print('maximum threads: {}'.format(self.threads.maxThreadCount()))

    def closeEvent(self, event: QEvent) -> None:
        if self.threads.activeThreadCount() > 0:
            QMessageBox.critical(self, '任务正在执行', '还有进程在运行,不允许关闭',
                                 QMessageBox.StandardButton.Ignore)
        event.ignore()  # 忽略关闭事件

    def update_progress_value(self, value: int) -> None:
        percentage = (value / 1000) * 100  # 计算百分比
        self.label.setText(f'{percentage:.1f}%')
        self.progress_bar.setValue(value)

    def handle_worker_begin(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(False)
        self.active_workers += 1
        print('BEGIN thread_id=', thread_id, ', time_str =', time_str)

    def handle_worker_finished(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(True)
        self.active_workers -= 1
        print('FINISHED thread_id=', thread_id, ', time_str =', time_str)

    def start_threads(self) -> None:
        worker = MyWorker()
        worker.signals.begin.connect(self.handle_worker_begin)
        worker.signals.finished.connect(self.handle_worker_finished)
        worker.signals.progress_value.connect(self.update_progress_value)
        self.threads.start(worker)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()

示例效果

主窗口关闭检测与拦截
主窗口关闭检测与拦截

此时只能强制退出程序,且退出时仍然会有报错:

强制退出程序导致的报错
强制退出程序导致的报错

优化三:优雅退出程序

示例代码

代码语言:python
代码运行次数:0
运行
AI代码解释
复制
from __future__ import annotations

import sys
import threading
import time
from datetime import datetime
from random import randint

from PySide6.QtCore import QEvent, QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QProgressBar, QPushButton, QVBoxLayout, QWidget


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class WorkerSignals(QObject):
    begin = Signal(str, str)
    finished = Signal(str, str)
    progress_value = Signal(int)  # 进度值


class MyWorker(QRunnable):
    def __init__(self, *args, **kwargs):
        # 传递参数给 Worker
        super().__init__()
        print('args=', args, 'kwargs=', kwargs)
        # 初始化信号和槽
        self.signals = WorkerSignals()
        self.args = args
        self.kwargs = kwargs

        self.killed = False

    @Slot()
    def run(self) -> None:
        thread_id = str(threading.get_ident())
        # 开始工作
        self.signals.begin.emit(thread_id, get_time_str())
        total_progress = 1000
        for i in range(total_progress):
            if self.killed:
                print('worker quit....')
                return
            time.sleep(float(randint(100, 1000)) / 1000)  # 模拟耗时操作
            self.signals.progress_value.emit(i)  # 发送信号,更新进度值
        # 结束工作
        self.signals.finished.emit(thread_id, get_time_str())

    def kill(self) -> None:
        print('set worker to killed')
        self.killed = True


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.active_workers = 0
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 1000)
        self.label = QLabel('0.0%')
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.start_threads)

        self.worker = MyWorker()  # 创建一个 Worker 对象

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.progress_bar)
        v_layout.addWidget(self.label)
        v_layout.addWidget(self.button)

        container = QWidget()
        container.setLayout(v_layout)

        self.setCentralWidget(container)

        self.threads = QThreadPool()
        print('maximum threads: {}'.format(self.threads.maxThreadCount()))

    def closeEvent(self, event: QEvent) -> None:
        if self.active_workers > 0:
            self.worker.kill()
            self.threads.waitForDone()
            self.active_workers -= 1
            self.worker = None
        event.accept()

    def update_progress_value(self, value: int) -> None:
        percentage = (value / 1000) * 100  # 计算百分比
        self.label.setText(f'{percentage:.1f}%')
        self.progress_bar.setValue(value)

    def handle_worker_begin(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(False)
        self.active_workers += 1
        print('BEGIN thread_id=', thread_id, ', time_str =', time_str)

    def handle_worker_finished(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(True)
        self.active_workers -= 1
        print('FINISHED thread_id=', thread_id, ', time_str =', time_str)

    def start_threads(self) -> None:
        if self.worker is not None:
            self.worker.signals.begin.connect(self.handle_worker_begin)
            self.worker.signals.finished.connect(self.handle_worker_finished)
            self.worker.signals.progress_value.connect(self.update_progress_value)
            self.threads.start(self.worker)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()
退出触发逻辑
退出触发逻辑
退出的事件捕获与清理
退出的事件捕获与清理

示例效果

优雅退出主程序
优雅退出主程序

优化四:进度条暂停

示例代码

代码语言:python
代码运行次数:1
运行
AI代码解释
复制
from __future__ import annotations

import sys
import threading
import time
from datetime import datetime
from random import randint

from PySide6.QtCore import QEvent, QObject, QRunnable, QThreadPool, Signal, Slot
from PySide6.QtWidgets import QApplication, QLabel, QMainWindow, QProgressBar, QPushButton, QVBoxLayout, QWidget


def get_time_str() -> str:
    return datetime.now().isoformat(sep = ' ')


class WorkerSignals(QObject):
    begin = Signal(str, str)
    finished = Signal(str, str)
    progress_value = Signal(int)  # 进度值


class MyWorker(QRunnable):
    def __init__(self, *args, **kwargs):
        # 传递参数给 Worker
        super().__init__()
        print('args=', args, 'kwargs=', kwargs)
        # 初始化信号和槽
        self.signals = WorkerSignals()
        self.args = args
        self.kwargs = kwargs

        self.killed = False
        self.paused = False

    @Slot()
    def run(self) -> None:
        thread_id = str(threading.get_ident())
        # 开始工作
        self.signals.begin.emit(thread_id, get_time_str())
        total_progress = 1000
        for i in range(total_progress + 1):
            while self.paused:
                time.sleep(0)
            if self.killed:
                print('worker quit....')
                return
            time.sleep(float(randint(1, 10)) / 1000)  # 模拟耗时操作
            self.signals.progress_value.emit(i)  # 发送信号,更新进度值
        # 结束工作
        self.signals.finished.emit(thread_id, get_time_str())

    def kill(self) -> None:
        print('set worker to killed')
        self.killed = True

    def pause(self) -> None:
        self.paused = True

    def pause_resume(self) -> None:
        self.paused = False

    def is_paused(self) -> bool:
        return self.paused


class MyMainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.active_workers = 0
        self.setWindowTitle('Hello, PySide6!')
        self.setToolTip('A PySide6 GUI Application Demo')
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 1000)
        self.label = QLabel('0.0%')
        self.button = QPushButton('Start')
        self.button.clicked.connect(self.start_threads)

        self.pause_button = QPushButton('Pause')
        self.pause_button.clicked.connect(self.pause_resume)
        self.pause_button.setEnabled(False)

        self.worker = MyWorker()  # 创建一个 Worker 对象

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.progress_bar)
        v_layout.addWidget(self.label)
        v_layout.addWidget(self.button)
        v_layout.addWidget(self.pause_button)

        container = QWidget()
        container.setLayout(v_layout)

        self.setCentralWidget(container)

        self.threads = QThreadPool()
        print('maximum threads: {}'.format(self.threads.maxThreadCount()))

    def closeEvent(self, event: QEvent) -> None:
        if self.active_workers > 0:
            self.worker.kill()
            self.worker.pause_resume()
            self.threads.waitForDone()
            self.active_workers -= 1
            self.worker = None
        event.accept()

    def update_progress_value(self, value: int) -> None:
        if self.worker is not None and not self.worker.is_paused():
            percentage = (value / 1000) * 100  # 计算百分比
            self.label.setText(f'{percentage:.1f}%')
            self.progress_bar.setValue(value)

    def handle_worker_begin(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(False)
        self.active_workers += 1
        print('BEGIN thread_id=', thread_id, ', time_str =', time_str)

    def handle_worker_finished(self, thread_id: str, time_str: str) -> None:
        self.button.setEnabled(True)
        self.active_workers -= 1
        print('FINISHED thread_id=', thread_id, ', time_str =', time_str)
        self.worker = MyWorker()  # 创建一个 Worker 对象
        self.pause_button.setEnabled(False)

    def start_threads(self) -> None:
        if self.worker is not None:
            self.worker.signals.begin.connect(self.handle_worker_begin)
            self.worker.signals.finished.connect(self.handle_worker_finished)
            self.worker.signals.progress_value.connect(self.update_progress_value)
            self.threads.start(self.worker)
            self.pause_button.setEnabled(True)

    def pause_resume(self) -> None:
        if self.worker is not None:
            if not self.worker.is_paused():
                self.worker.pause()
                self.pause_button.setText('Resume')
            else:
                self.worker.pause_resume()
                self.pause_button.setText('Pause')


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MyMainWindow()
    window.show()
    app.exec()
暂停逻辑
暂停逻辑
退出逻辑
退出逻辑

示例效果

进度条暂停
进度条暂停
进度条完成效果
进度条完成效果

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
PySide6 GUI 编程(38):信号拦截与 lambda 槽函数
在之前的文章:PySide6 GUI 编程(3):信号槽机制中已经探讨过关于自定义信号的场景。在一些更追求灵活性的场景下,我们需要自定义信号,以此触发更多自定义的行为。
bowenerchen
2024/08/31
9540
PySide6 GUI 编程(38):信号拦截与 lambda 槽函数
PySide6 GUI 编程(32):QMouseEvent 鼠标事件
bowenerchen
2024/08/23
4641
PySide6 GUI 编程(32):QMouseEvent 鼠标事件
PySide6 GUI 编程(40):MVC 设计原则下QListView的使用
代码应该遵循MVC(模型-视图-控制器)设计原则,将数据模型、视图和控制器分离,以便于维护和扩展。
bowenerchen
2024/09/01
4011
PySide6 GUI 编程(40):MVC 设计原则下QListView的使用
PySide6 GUI 编程(28):QProgressDialog进度条
当进度值累加到最大值或超过最大值时,QProgressDialog会将进度值重置为-1
bowenerchen
2024/08/21
4682
PySide6 GUI 编程(28):QProgressDialog进度条
PySide6 GUI 编程(7):QLabel的使用
bowenerchen
2024/07/20
4071
PySide6 GUI 编程(7):QLabel的使用
PySide6 GUI 编程(29):QWizard 的简单示例
bowenerchen
2024/08/21
2283
PySide6 GUI 编程(29):QWizard 的简单示例
PySide6 GUI 编程(31):多个 QWidget 窗口展示
进程在刚启动时会初始化出两个窗口,这可以证明 init_new_window() 返回的对象是可以正常展示的
bowenerchen
2024/08/21
6063
PySide6 GUI 编程(31):多个 QWidget 窗口展示
PySide6 GUI 编程(26):QInputDialog的简单使用
bowenerchen
2024/08/19
3951
PySide6 GUI 编程(26):QInputDialog的简单使用
【Pyside6】Python多线程实现的选择与QThread的推荐实现方式
因为在网络上,特别是中文互联网上,关于Pyside6多线程的写法,特别是QThread的使用提及比较少,且较多使用不太推荐的写法,这篇博客主要是存下我自己参考的博客,希望对大家也有帮助。
Livinfly
2023/03/16
5K0
【Pyside6】Python多线程实现的选择与QThread的推荐实现方式
PySide6 GUI 编程(25):QMessageBox的简单使用
bowenerchen
2024/08/12
4152
PySide6 GUI 编程(25):QMessageBox的简单使用
PySide6 GUI 编程(24):QDialog以及QDialogButtonBox
QDialog 自身运行时会触发一个事件循环, 这个事件循环与 QApplication 的事件循环并没有显著的从属关系,可以认为它们是独立的。
bowenerchen
2024/08/08
5232
PySide6 GUI 编程(24):QDialog以及QDialogButtonBox
PySide6 GUI 编程(17):QVBoxLayout / QHBoxLayout 的简单用法
bowenerchen
2024/07/29
3651
PySide6 GUI 编程(17):QVBoxLayout / QHBoxLayout 的简单用法
PySide6 GUI 编程(27):QFileDialog的简单使用
bowenerchen
2024/08/21
3861
PySide6 GUI 编程(27):QFileDialog的简单使用
PySide6 GUI 编程(30):其他常见对话框
bowenerchen
2024/08/21
2212
PySide6 GUI 编程(30):其他常见对话框
PySide6 GUI 编程(3):信号槽机制
信号的重载在 PySide6 中并不推荐使用,对于从 QT5 遗留的代码可以继续使用这一能力。
bowenerchen
2024/05/19
6071
PySide6 GUI 编程(3):信号槽机制
Python写的嗅探器——Pyside,Scapy
使用Python的Pyside和Scapy写的嗅探器原型,拥有基本框架,但是功能并不十分完善,供参考。 1 import sys 2 import time 3 import binasc
ascii0x03
2018/04/12
2K0
Python写的嗅探器——Pyside,Scapy
PySide6 GUI 编程(47): 基于QProcess运行外部命令
QProcess 可以用来启动外部程序。这在需要从 Qt 应用中运行第三方工具或命令行程序时非常有用。例如,一个图形用户界面(GUI)应用可能需要调用一个命令行工具来处理数据或执行某些操作。
bowenerchen
2024/09/18
3232
PySide6 GUI 编程(47): 基于QProcess运行外部命令
PySide6 GUI 编程(9):QComboBox的使用
在 macOS 系统上,QComboBox 的 setPlaceholderText 方法可能不会按预期工作,因为 macOS 的用户界面指南通常不支持在组合框中使用占位符文本。
bowenerchen
2024/07/21
4861
PySide6 GUI 编程(9):QComboBox的使用
PySide6 GUI 编程(6):QPushButton的使用
bowenerchen
2024/07/20
4542
PySide6 GUI 编程(6):QPushButton的使用
PySide6 GUI 编程(8):QCheckBox的使用
bowenerchen
2024/07/21
3931
PySide6 GUI 编程(8):QCheckBox的使用
推荐阅读
相关推荐
PySide6 GUI 编程(38):信号拦截与 lambda 槽函数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验