zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Pyside6/PyQT 项目实战,从0开始写一个GUI可视化项目:总览

项目 一个 实战 可视化 开始 GUI Pyqt 总览
2023-09-27 14:26:45 时间

在这里插入图片描述

前言

最近使用 Pyside6 编写了几个GUI工具,发现效果出奇的好。遂产生了分享它的念头。
接下来如果不出意外,大概没有意外,我会开始写这个专栏,介绍从零开始去编写一个实用的GUI工具。
这是Pyside6第一篇:《总览》

本文对Pyside6在开发使用中进行了知识点的提炼,所以后面本专栏更新文章内容大致就是针对本文的每个小内容做一些拓展。

建议有需要的小伙伴通过官方文档去进行系统地学习!!
Pyside6 文档:https://doc.qt.io/qtforpython/

后面专栏新增文章时候,本文会做出相应修改!!
专栏整体大概在10篇往上,反正学了你就能使用Pyside6 编写自己的GUI工具了。

先在此大放厥词,本专栏无论是质量,还是质量,都是市面上你能找到的最好的!!!
效果是市面上最好的,欢迎各种打脸
效果是市面上最好的,欢迎各种打脸
效果是市面上最好的,欢迎各种打脸


专栏脉络

专栏内容大体如下,会酌情增加一些使用技巧以及方法。

初级文章暂定0篇,后续有空再慢慢更新;
进阶文章暂定10篇,后续看实际情况再增加;

《初阶文章》基本操作系列

  • 待更新 大概率不会分享基础操作,选择性地分享一些PySide6使用中的小技巧
  • 待更新 如使用designer.exe绘制等比例缩放的窗口
  • 待更新 如鼠标悬停提示
  • 待更新 如窗口阴影、窗口状态栏
  • 待更新 如文件的导入的常用方法、文件拖拽
  • 待更新 如部分控件的指定操作,如单击、双击、拖拽
  • 待更新 如应用程序适应屏幕缩放
  • 待更新 如制作流畅的窗口控件动画,组合动画
  • 待更新 如使用MVC模型去拆解代码
  • 待更新 如打包自己的PySide6/PyQT程序
  • 待更新 以上的操作等,都没有技术上的难度。只有操作上的难度~
  • 待更新 但最难的还是UI设置,笔者实在是没有审美,只有审丑
  • 待更新 暂待定,更新了效果图再说~

《进阶文章》多线程系列操作

“提升 PySide6 应用性能与响应性:多线程编程简介”
“深入理解多线程概念:为 PySide6 编程奠定基础”
“PySide6 中的多线程支持:QThread、QRunnable 和信号与槽”
“实战多线程编程模式:在 PySide6 中应用生产者-消费者、并行计算等模式”
“PySide6 多线程编程技巧:实现线程安全、避免竞态条件和错误处理”
“管理和控制多线程:在 PySide6 中实现暂停、恢复和停止线程”
“注意事项与解决方案:PySide6 多线程编程中需注意的问题”
“实例演示:PySide6 多线程编程的应用场景和示例代码”
“进阶优化:提高 PySide6 多线程应用的性能和效率”

基本流程

  1. 布局(通过designer.exe 手动绘制
  2. 编写逻辑
  3. 将布局展示

前置操作

安装模块

pip install pyside6

文件转换

关于pyside6文件:https://doc.qt.io/qtforpython/tutorials/pretutorial/typesoffiles.html

  • ui文件:布局文件,基于 XML 的格式
  • qrc文件:Qt Recources file,是一个 XML 格式的资源配置文件

ui to py

pyside2-uic xxx.ui -o xxx.py

qrc to py

pyside2-rcc xxx.qrc -o xxx.py

默认模板

官方展示的案例

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

if __name__ == "__main__":
    app = QApplication(sys.argv)
    label = QLabel("Hello World", alignment=Qt.AlignCenter)
    label.show()
    sys.exit(app.exec_())

加载 ui 有两种方式,

  • 一种是直接加载ui
  • 一种是将ui转成py,然后再加载py

直接加载ui

  • 不好用,不做展示。

加载py

from PySide2.QtWidgets import QApplication, QMainWindow

from demo_ui import Ui_Form


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


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

设置焦点

鼠标点击某个组件就执行指定操作时候,可以用到这一步。结合 鼠标点击事件

方法一:

designer 中,选中对应的组件,

属性编辑器 -> focusPolicy -> ClickFocus

后面当鼠标点击在该组件时候,ui当前的焦点就在该组件上。

方法二:

self.QWidget.setFocusPolicy(Qt.ClickFocus)

固定界面大小

self.setFixedSize(self.width(), self.height())

TableWidget

设置行数

TableWidget.setRowCount(int())	# 输入int

不显示行号

TableWidget.verticalHeader().setVisible(False)

列可拖拽

TableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)

均分列的宽度

TableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

根据内容长度分配列宽

  • 两句一起用,效果更好
TableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
# 指定第0列
TableWidget.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeToContents)
# 也可以不指定列,作用于所有列
TableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

Table显示

from PySide2.QtWidgets import QTableWidgetItem

data = [('c1', 'c2', 'c3'), ('d1', 'd2', 'd3')...]
for row, row_data in enumerate(data):
    for columns, columns_data in enumerate(row_data):
        TableWidget.setItem(row, columns, QTableWidgetItem(str(columns_data)))

表格复制

def __init__(self):
    # 剪切板
    self.cb = QtWidgets.QApplication.clipboard()
    ...
    # 单击表格单元格,即黏贴到剪切板
    self.table_show.clicked.connect(lambda: self.cb.setText(self.QTableWidget.currentItem().text()))



def keyPressEvent(self, event):
    """ Ctrl + C复制表格内容 """
    if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_C:
        # 获取表格的选中行
        # 只取第一个数据块,其他的如果需要要做遍历,简单功能就不写得那么复杂了
        selected_ranges = self.QTableWidget.selectedRanges()[0]  
        # 最后总的内容
        text_str = ""  
        # 行(选中的行信息读取)
        for _row in range(selected_ranges.topRow(), selected_ranges.bottomRow() + 1):
            row_str = ""
            # 列(选中的列信息读取)
            for col in range(selected_ranges.leftColumn(), selected_ranges.rightColumn() + 1):
                item = self.QTableWidget.item(_row, col)
                # 制表符间隔数据
                row_str += item.text() + '\t'  
            # 换行	
            text_str += row_str + '\n' 
        self.cb.setText(text_str)

鼠标悬停文字提示

from PySide2.QtCore import Qt
from PySide2.QtGui import QFont

QToolTip.setFont(QFont('SansSerif', 10))
self.button_run.setToolTip('点击该按钮可以<br><b>打开与关闭数据预览</b>')

QFileDialog

导入文件&文件夹

from PySide2.QtWidgets import QFileDialog

# 对应的,做一些格式的筛选
path = QFileDialog.getOpenFileName(self, '选择文件', '.py', 'Python Files (*.py)')[0]

# 选择多个文件
path = QFileDialog.getOpenFileNames(self, '选择文件', '.py', 'Python Files (*.py)')[0]

导出文件

QFileDialog.getSaveFileName(self, '保存文档', 'untitled.xlsx', 'excel文件 (*.xls *.xlsx)')[0]

QMessageBox

提示弹窗

from PySide2.QtWidgets import QFileDialog

QMessageBox.information(self, '提示', '这是提示弹窗')

可选提示弹窗

res = QMessageBox.question(self, 'Message', '确定要退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if res == QMessageBox.Yes:
    print('你选择了是.')
else:
    print('你选择了否.')

radioButton

清空选择

QRadioButton.setAutoExclusive(False)
QRadioButton.setChecked(False)
QRadioButton.setAutoExclusive(True)

事件监听

文件拖拽

# 设置文件支持拖拽
self.setAcceptDrops(True)

def dragEnterEvent(self, event) -> None:
    """文件拖拽事件"""
    if event.mimeData().hasText():
        # 获取拖拽进来的文件路径
        file_path = event.mimeData().urls()[0].toLocalFile()
        # 鼠标放开函数事件
        event.accept()
        # do something
    else:
        event.ignore()

关闭事件

def closeEvent(self, event) -> None:
    """关闭事件"""
    res = QMessageBox.question(self, 'Message', '确定要退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    if res == QMessageBox.Yes:
        event.accept()
    else:
        event.ignore()

鼠标点击事件

def mousePressEvent(self, event) -> None:
    """鼠标点击事件"""
    # 判定是左键点击
    if event.button() == Qt.MouseButton.LeftButton:
        time.sleep(1)
        self.import_file()
    return
self.focusWidget().objectName() == 'groupBox'

窗口可拖拽

  • 重写3个函数,
from PySide2.QtCore import Qt, QPoint

def __init__(self):
    # 窗口移动、设置鼠标动作位置
    self._move = False
    self.m_position = QPoint(0, 0)


# 鼠标点击事件产生
def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        self._move = True
        self.m_position = event.globalPos() - self.pos()
        event.accept()

# 鼠标移动事件
def mouseMoveEvent(self, QMouseEvent):
    if Qt.LeftButton and self._move:
        self.move(QMouseEvent.globalPos() - self.m_position)
        QMouseEvent.accept()

# 鼠标释放事件
def mouseReleaseEvent(self, QMouseEvent):
    self._move = False

隐藏边框、阴影效果

from PySide2.QtCore import Qt
from PySide2.QtWidgets import QGraphicsDropShadowEffect


def __init__(self):
    # 隐藏边框
    self.setWindowFlags(Qt.FramelessWindowHint)
    self.setAttribute(Qt.WA_TranslucentBackground)

    # 阴影效果
    effect = QGraphicsDropShadowEffect(self)
    effect.setBlurRadius(30)
    effect.setOffset(0, 0)
    effect.setColor(Qt.gray)
    self.setGraphicsEffect(effect)

状态栏图标

from ctypes import windll

# 这段代码放在前面即可
try:
    myapp_id = 'mycompany.myproduct.subproduct.version'
    windll.shell32.SetCurrentProcessExplicitAppUserModelID(myapp_id)
except ImportError:
    pass


if __name__ == "__main__":
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QApplication([])
    # 指定状态栏和程序左上角的图标,需要绝对路径
	app.setWindowIcon(QtGui.QIcon(r'C:\User\Desktop\icon.ico'))
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

退出所有进程

closeEvent 关闭事件里,退出主线程连带退出子线程。

  • 添加 os._exit(0)
def closeEvent(self, event) -> None:
    """关闭事件"""
    res = QMessageBox.question(self, 'Message', '确定要退出吗?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    if res == QMessageBox.Yes:
        event.accept()
        os._exit(0)
    else:
        event.ignore()

而这有一个前提,就是子线程需要为 threading.Thread 创建的新线程,
如下所示:

from threading import Thread


def new_thread():
    t = Thread(target=demo, args=())
    t.start()

def demo():
    ...

打包成 .exe

后话

本次分享远远未结束!!
建议关注本专栏,以获得文章更新的最新消息哦!!
🐱‍🏍🐱‍🏍