草庐IT

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

是小菜欸 2023-08-12 原文

前言

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

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

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

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


专栏脉络

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

基本流程

  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

后话

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

有关Pyside6项目实战,从0开始写一个GUI可视化项目:总览的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  5. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  8. ruby-on-rails - 新 Rails 项目 : 'bundle install' can't install rails in gemfile - 2

    我已经像这样安装了一个新的Rails项目:$railsnewsite它执行并到达:bundleinstall但是当它似乎尝试安装依赖项时我得到了这个错误Gem::Ext::BuildError:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcheckingforlibkern/OSAtomic.h...yescreatingMakefilemake"DESTDIR="cleanmake"DESTDIR="

  9. ruby-on-rails - Rails - 从另一个模型中创建一个模型的实例 - 2

    我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案

  10. ruby - Ruby 中的波形可视化 - 2

    我即将开始一个将录制和编辑音频文件的项目,我正在寻找一个好的库(最好是Ruby,但会考虑Java或.NET以外的任何库)以进行实时可视化波形。有人知道我应该从哪里开始搜索吗? 最佳答案 要流入浏览器的数据量很大。Flash或Flex图表可能是唯一能提高内存效率的解决方案。Javascript图表往往会因大型数据集而崩溃。 关于ruby-Ruby中的波形可视化,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.c

随机推荐