Pyside6实践_1


做实验的时候想拷贝电子参考书代码的时候发现代码都数字序号,手动删除太麻烦了,于是边想着写一个python脚本删除序号,然后不知道怎么突发奇想想试试Python GUI。

也是第一次接触,故记录一下。

源代码

import sys,re
from PySide6.QtWidgets import (QLineEdit, QPushButton, QApplication,
    QVBoxLayout, QDialog,QLabel,QPlainTextEdit,QMessageBox,QGridLayout)
from PySide6.QtCore import Qt
from PySide6.QtGui import QIcon
from PySide6.QtGui import QKeyEvent,QTextCursor,QGuiApplication

class CustomPlainTextEdit(QPlainTextEdit):
    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == int(Qt.Key_Return) or event.key() == int(Qt.Key_Enter):
            self.moveCursor(QTextCursor.End)
            self.insertPlainText('\n')
            return

        super().keyPressEvent(event)


class Form(QDialog):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent) #parent指定对话框的父窗口
        self.label = QLabel("请输入文本,将帮您实现去除每行文本开头数字序号和空格:")
        # 创建组件
        self.edit = CustomPlainTextEdit()
        self.edit.setFixedWidth(450)  # 设置宽度为200像素
        self.edit.setFixedHeight(200)  # 设置高度为30像素
        #self.edit.setAlignment(Qt.AlignTop)  # 将文本对齐方式设置为顶部对齐
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        self.button = QPushButton("Run")
        self.button_copy = QPushButton("一键复制结果")

        self.button_copy.clicked.connect(self.copy_text)
        # 创建布局并添加组件
        layout = QGridLayout()
        layout.addWidget(self.label,0,0)
        layout.addWidget(self.button_copy,2,1)
        layout.addWidget(self.edit,1,0)
        layout.addWidget(self.button,2,0)
        # 设置窗口图标
        icon = QIcon("logo.ico")  # 替换为自己的图标路径
        self.setWindowIcon(icon)
        self.setWindowTitle("Wq's tool")
        # 应用布局
        self.setLayout(layout)
        # 连接greetings槽和按钮单击信号
        self.button.clicked.connect(self.greetings)

    # 实现对输入文本的处理,由于是对话框的输出,保持原来的函数名字greetings()
    def greetings(self):
        #这里在给self.lines赋值是因为似乎之前定义之前没按钮没生效,之前定义应该赋空值,懒得改了,没什么错误那就保持原样吧。。。
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        for i,line in enumerate(self.lines):
            #调用re.sub(正则表达式,替换后的字符,需要替换掉原味奶)
            self.lines[i] = re.sub(r'^\d+\s*', '', line)
        #这里再开一个窗口
        message_box = QMessageBox()
        message_box.setWindowTitle("出た!")
        # 这里还有几个默认的参数没放出来
        # message_box.setIcon(QMessageBox.Information) 默认提示框图标为信息
        # message_box.addButton(QMessageBox.Ok) 默认有一个ok键
        output_text = ""  # 初始化空字符串

        for line in self.lines:
            output_text += line
            output_text += "\n"
        message_box.setText(output_text)
        message_box.setFixedSize(400, 300)
        message_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)# 设置消息框的文本格式为可选择和可复制
        message_box.exec()

    def copy_text(self):
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        for i, line in enumerate(self.lines):
            self.lines[i] = re.sub(r'^\d+\s*', '', line)
        clipboard = QGuiApplication.clipboard()
        output_text = ""  # 初始化空字符串

        for line in self.lines:
            output_text += line
            output_text += "\n"
        clipboard.setText(output_text)  # 将输出文本设置到剪贴板

if __name__ == '__main__':
    # 创建Qt应用程序
    app = QApplication(sys.argv)
    # 创建并显示Form
    form = Form()
    form.show()
    # 运行Qt主循环
    sys.exit(app.exec_())

效果截图

image-20231019215938556

image-20231019220033073

一键复制功能就不展示了,按下按钮即可复制。

虽然对我来说很艰巨,但是放在github上面还是显得和大佬格格不入,自己记录一下就好了。

捋一下思路

先创建Qt应用程序

app = QApplication(sys.argv)

创建并显示Form(创建对话框窗口。QDialog)

form = Form()
class Form(QDialog): #表示创建的是QDialog的子类

    def __init__(self, parent=None):
        super(Form, self).__init__(parent) #parent指定对话框的父窗口,None即无父窗口
        self.label = QLabel("请输入文本,将帮您实现去除每行文本开头数字序号和空格:")
        # 创建组件
        self.edit = CustomPlainTextEdit()
        self.edit.setFixedWidth(450)  # 设置宽度为200像素
        self.edit.setFixedHeight(200)  # 设置高度为30像素
        #原本使用的是self.edit = QLineEdit(),故需要设置顶部对齐,但好像按enter不能换行
        #self.edit.setAlignment(Qt.AlignTop)  # 将文本对齐方式设置为顶部对齐
        #设置输入框里内容为self.line并且split("\n")
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        #set functional button
        self.button = QPushButton("Run")
        self.button_copy = QPushButton("一键复制结果")
        #将复制键和self.copy_text函数关联起来
        self.button_copy.clicked.connect(self.copy_text)
        # 创建布局并添加组件
        # 这里我原本使用的是QVBoxlayout即垂直分布,另还有QHBoxlayout
        layout = QGridLayout()
        layout.addWidget(self.label,0,0)
        layout.addWidget(self.button_copy,2,1)
        layout.addWidget(self.edit,1,0)
        layout.addWidget(self.button,2,0)
        # 设置窗口图标
        icon = QIcon("logo.ico")  # 替换为自己的图标路径
        self.setWindowIcon(icon)
        self.setWindowTitle("Wq's tool")
        # 应用布局
        self.setLayout(layout)
        # self.button连接greetings槽和按钮单击信号
        self.button.clicked.connect(self.greetings)

class Form(QDialog):

  • Form 类继承自 QDialog 类,意味着 Form 类将继承 QDialog 类的属性和方法,并且可以在此基础上添加自己的特定行为和属性。

  • QDialog 是Qt框架中提供的一个类,用于创建对话框窗口。对话框是一种常见的用户界面组件,用于与用户进行交互,接受输入或显示信息。

  • Form 类定义为 QDialog 的子类,你可以扩展 QDialog 类提供的功能,以满足特定的需求。可以添加自定义的布局、控件和逻辑,以及重写和扩展 QDialog 类中的方法来实现定制化的对话框窗口。

补充:在Python中,super() 函数是用于调用父类(超类)的方法。它的作用是获取当前类的父类,并调用父类的方法。super(Form, self).__init__(parent) 的作用是调用 Form 类的父类 QDialog__init__ 方法,并传递 parent 参数给它。Python中,如果一个类继承自另一个类,那么在子类中重写父类的方法时,可以使用 super() 来调用父类的同名方法并扩展其功能。在Python 3及以上的版本中,可以使用简化的语法 super().__init__(parent) 来调用父类的方法,而不需要显式地指定类名和实例对象。但在旧版本的Python中,仍然需要使用 super(Form, self).__init__(parent) 的形式来调用父类的方法。

QGridLayout 是一种布局管理器,用于将窗口或小部件按照网格的形式进行排列。

常用的布局管理器:

  1. QVBoxLayout:垂直布局管理器,将小部件垂直排列。
  2. QHBoxLayout:水平布局管理器,将小部件水平排列。
  3. QFormLayout:表单布局管理器,用于创建标签和输入字段的组合。
  4. QStackedLayout:堆叠布局管理器,用于在同一区域显示多个小部件,但一次只显示一个小部件。
  5. QBoxLayout:通用的盒式布局管理器,可以将小部件水平或垂直排列。
  6. QGridLayout:网格布局管理器,以网格形式将小部件排列。
  7. QSizePolicy:调整小部件大小策略的类,可与其他布局管理器一起使用。

这些布局管理器可以根据您的需求进行嵌套和组合使用,以创建复杂的界面布局。您可以根据小部件的类型和排列需求选择适当的布局管理器来实现所需的布局效果。

然后实现与Run键相关联的函数/槽

def greetings(self):
        #这里在给self.lines赋值是因为似乎之前定义之前没按钮没生效,之前定义应该赋空值,懒得改了,没什么错误那就保持原样吧。。。
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        for i,line in enumerate(self.lines):
            #调用re.sub(正则表达式,替换后的字符,需要替换掉原味奶)
            self.lines[i] = re.sub(r'^\d+\s*', '', line)
        #这里再开一个窗口
        message_box = QMessageBox()
        message_box.setWindowTitle("出た!")
        # 这里还有几个默认的参数没放出来
        # message_box.setIcon(QMessageBox.Information) 默认提示框图标为信息
        # message_box.addButton(QMessageBox.Ok) 默认有一个ok键
        output_text = ""  # 初始化空字符串

        for line in self.lines: #将要输出的信息循环赋值给output_text
            output_text += line
            output_text += "\n"
        message_box.setText(output_text)
        message_box.setFixedSize(400, 300)
        message_box.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard)# 设置消息框的文本格式为可选择和可复制
        message_box.exec()

实现与“一键复制”关联的函数/槽

def copy_text(self):
        self.lines = [_ for _ in self.edit.toPlainText().split("\n")]
        for i, line in enumerate(self.lines):
            self.lines[i] = re.sub(r'^\d+\s*', '', line)
        clipboard = QGuiApplication.clipboard()
        output_text = ""  # 初始化空字符串

        for line in self.lines:
            output_text += line
            output_text += "\n"
        clipboard.setText(output_text)  # 将输出文本设置到剪贴板

clipboard = QGuiApplication.clipboard()

clipboard.setText(output_text) # 将输出文本设置到剪贴板

这是实现将文本复制到clipboard的核心代码

下面这个是自定义的 QPlainTextEdit 类,重写了 keyPressEvent() 方法以处理按键事件。在这个自定义类中,当用户按下回车键(Enter 键或 Return 键)时,会将光标移动到文本的末尾,并在末尾插入一个换行符。

class CustomPlainTextEdit(QPlainTextEdit):
    def keyPressEvent(self, event: QKeyEvent):
        if event.key() == int(Qt.Key_Return) or event.key() == int(Qt.Key_Enter):
            self.moveCursor(QTextCursor.End)# 将光标移动到文本末尾
            self.insertPlainText('\n')
            return
# 如果按下的不是回车键,则调用父类的 keyPressEvent() 方法处理其他按键事件
        super().keyPressEvent(event)

在这个自定义类中,我们重写了 keyPressEvent() 方法,并在方法体内对按键事件进行处理。如果按下的键是回车键(Enter 键或 Return 键),则会执行 if 语句块中的操作:将光标移动到文本的末尾,并在末尾插入一个换行符。然后,通过 return 语句提前结束方法的执行。

如果按下的键不是回车键,则会调用父类的 keyPressEvent() 方法,以便处理其他按键事件的默认行为。

使用Nuitka

nuitka --standalone --show-memory --show-progress --windows-disable-console --nofollow-imports --plugin-enable=pyside6 --follow-import-to=utils,src --output-dir=out --windows-icon-from-ico=./logo.ico demo.py  
  • --standalone:方便移植到其他机器,不用再安装python

  • --show-memory --show-progress:展示整个安装的进度过程

  • --windows-disable-console:运行exe取消弹框。如果我们还需要调试,可能哪里还有问题之类的,就不用放上去了,但一般问题都在Pycharm解决了,就加上去

  • --nofollow-imports:不编译代码中所有的import,比如keras,numpy之类的。

  • --plugin-enable=qt-plugins:我这里用到pyside6来做界面的,这里nuitka有其对应的插件。还有pyqt之类的

  • --follow-import-to=utils,src:需要编译成C++代码的指定的2个包含源码的文件夹,这里用,来进行分隔。

  • --output-dir=out:指定输出的结果路径或者说输出文件夹名为out。

  • --windows-icon-from-ico=./logo.ico:指定生成的exe的图标为logo.ico这个图标,这里使用的将图片转成ico格式文件的网站(比特虫)。

如果发现真正运行exe的时候,会报错:no module named torch,cv2,tensorflow等等这些没有转成C++的第三方包。

这里需要找到这些包复制(比如numpy,cv2这个文件夹)到demo.dist路径下。

至此,exe能完美运行啦!

参考网站

其它可参考网站

最后感谢免费的innosetup生成安装包 innosetup

其它资源

pyside6 pythonguis

Designer写GUI


Author: 寒风渐微凉
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source 寒风渐微凉 !
 Previous
Next 
  TOC