当前位置: 首页 > news >正文

PyQt5实战——操作台打印重定向,主界面以及stacklayout使用(四)

个人博客:苏三有春的博客
系类往期文章:
PyQt5实战——多脚本集合包,前言与环境配置(一)
PyQt5实战——多脚本集合包,UI以及工程布局(二)
PyQt5实战——多脚本集合包,程序入口QMainWindow(三)

PyQt主窗口框架设计(QWidget)

PrimeWindow.py的主要作用是绘制主窗口,在主窗口中:

  • 最左边是各种功能组件的入口,比如:翻译器,UTF-8转换,图像处理等。
  • 按下左边的按键,则会切换功能区,切换功能并非重新开一个窗口,而是切换中间部分,这里主要是采用stack的原理
  • 最右边是日志打印区,通过重定向的方法,把打印在操作台的内容打印到这个窗口上,这样方便开发人员debug

4.1 import部分

import Classes.TranslatorClass as TranslatorClass
import Classes.ConvertorClass as ConvertorClass
import Classes.AudioProcessClass as AudioProcessClass
import Classes.AudiocodecClass as AudiocodecClass
import Classes.AudioPlayerClass as AudioPlayerClass
import Classes.ImgProcessClass as ImgProcessClass
from component.btnStyle import *
from component.editStyle import *

这些引用均来自作者自己的代码,分别保存在Classes目录下和component目录下,从这些目录下import不同的类或方法供PrimeWindow调用。

Classes中的各种类,如TranslatorClass,保存着翻译功能的功能区UI结构:翻译按钮,翻译内容输入框以及翻译结果展示框等,以及各组件的摆放位置。

component中的各种方法,如btnStyle,该文件中有按钮按下的样式,按钮抬起的样式等,只需在点击按钮时调用该方法即可改变按钮样式。

4.2 重定向操作台输出

重定向操作台输出需要调用一些系统方法

        current_directory = os.getcwd() # 获取当前工作目录,并将其存储在current_directory中。self.original_stdout = sys.stdout # 将当前的标准输出(操作台)保存到self.original_stdout中,这样可以在将来恢复标准输出self.output_stream = io.StringIO() # 创建一个StringIO对象,这个对象像一个文件,可以在内存中操作字符串,之后的标准输入输出将写入这个对象sys.stdout = self.output_stream # 将标准输出重定向到刚才创建的StringIO对象self.output_stream。这样,所有通过print语句输出的信息都将储存在output_stream中,而不是打印到操作台self.timer = QTimer() # 创建一个定时器对象,用于定期触发某些事件self.timer.timeout.connect(self.updateOutput) # 将定时器的信号连接到self.updateOutput方法上,当定时器达到超时时间时则会调用该方法self.timer.start(1000) # 设置定时器的超时时间为1000ms即1s

以上实现的是:重定向操作台的输出,开启定时器,每1s后,将原本pirnt方法打印到操作台的字符串储存起来,并调用updateOutput方法

以下是代码逐行分析:

  • 获取当前工作目录,并将其存储在current_directory中
  • 将当前的标准输出(操作台)保存到self.original_stdout中,这样可以在将来恢复标准输出
  • 创建一个StringIO对象,这个对象像一个文件,可以在内存中操作字符串,之后的标准输入输出将写入这个对象
  • 将标准输出重定向到刚才创建的StringIO对象self.output_stream。这样,所有通过print语句输出的信息都将储存在output_stream中,而不是打印到操作台
  • 创建一个定时器对象,用于定期触发某些事件
  • 将定时器的信号连接到self.updateOutput方法上,当定时器达到超时时间时则会调用该方法
  • 设置定时器的超时时间为1000ms即1s

updateOutput方法:

    def updateOutput(self):output = self.output_stream.getvalue() # 从output_stream中获取字符串if output: # 如果其中有字符串的话cursor = self.consoleedit.textCursor() # 获取日志打印文本编辑控件的当前光标,方便在特定的位置插入文本cursor.movePosition(QTextCursor.End) # 将光标移动至结尾,以便现有文本插入新的输出内容cursor.insertText(output) # 将内容添加到日志打印文本编辑控件中self.consoleedit.setTextCursor(cursor) # 更新光标位置为刚刚移动的光标,这确保了后续的输入或操作将从正确的位置开始self.consoleedit.ensureCursorVisible() # 确保光标在文本编辑器中可见,特别是光标在底部时,防止用户无法看到最新的插入内容,尤其是文本框不够大时self.output_stream.truncate(0) # 清空output_stream中的内容,将其内容截断为0,使得下一轮输出时不会将之前的内容重复添加

通过这个方法,即可将打印的内容输出到日志打印区中。并每1秒就检查一次是否有新的内容需要输出

  • 因为是利用定时器每1秒检查一次是否有新的输出内容,所以输出并不是实时进行的,如你需要更精确的日志打印,则调小定时器的超时时间,但请注意,定时器的超时时间越短,定时器就会越频繁地调用updateOutput方法,即使什么新内容也没有。如果你需要做一些复杂且精密的操作,则可能需要考虑时间与空间开销的问题。

以下是代码逐行分析:

  • 从output_stream中获取字符串
  • 如果其中有字符串的话
  • 获取日志打印文本编辑控件的当前光标,方便在特定的位置插入文本
  • 将光标移动至结尾,以便现有文本插入新的输出内容
  • 将内容添加到日志打印文本编辑控件中
  • 更新光标位置为刚刚移动的光标,这确保了后续的输入或操作将从正确的位置开始
  • 确保光标在文本编辑器中可见,特别是光标在底部时,防止用户无法看到最新的插入内容,尤其是文本框不够大时
  • 清空output_stream中的内容,将其内容截断为0,使得下一轮输出时不会将之前的内容重复添加

4.3 stackLayout 切换功能

在PyQt中,QStackLayout是用于在同一位置上堆叠多个小组件的布局管理器,允许根据需要在它们之间进行切换,这对实现标签页或动态内容展示非常有用

    def create_stack(self):# create a stack layoutself.stacklayout = QStackedLayout()convertor = ConvertorClass.Wconvertor()self.stacklayout.addWidget(convertor)
  • 创建stacklayout
  • 把功能UI实现的对象添加在stacklayout中
        # set the convertor buttonsself.UTF8ConvertorBtn = QPushButton("UTF-8 转换") # 创建按钮对象btnReleaseStyleA(self.UTF8ConvertorBtn) # 修改按钮样式,该方法为作者创建,并非第三方库调用self.UTF8ConvertorBtn.clicked.connect(self.Cbtn_press_clicked) # 为按钮连接触发事件,该事件会切换stack# add the buttons to the layoutself.btnlayout.addWidget(self.UTF8ConvertorBtn) # 将按钮添加到功能按钮区布局updateButtonStyle(self,self.UTF8ConvertorBtn) # 更新按钮样式,该方法为作者创建,并非第三方库调用

UTF8ConvertorBtn按钮连接上了Cbtn_press_clicked方法,表示当该按钮被按下时,则调用Cbtn_press_clicked方法。同时我对按钮的样式做了修改,使用方法btnReleaseStyleA,因为要对按钮做批量相同的修改,因此集成到一个方法中去调用是常见的编程思想。

  	def Cbtn_press_clicked(self):updateButtonStyle(self,self.UTF8ConvertorBtn) # 更新按钮样式,该方法为作者创建,非第三方库调用self.stacklayout.setCurrentIndex(0) # 将当前stack布局设置为第一个抽屉

setCurrentIndex方法的意思是,stacklayout所占据的这一片区域,可以随意切换成被它add了的widget,它就像一个抽屉一样,抽屉的大小固定,但是这个固定的大小所展示的内容是由你抽出第几层决定。就比如参数为0,则抽出的是第1个抽屉,这个抽屉是一个widget,这个widget可以套一个layout,其中放各种各样的组件。同理,参数为1,则表示抽出第2个抽屉,则stacklayout所占据的这一片区域就会切换成第2个抽屉的样子。

4.4 UI初始化

    def initUI(self):#---------------- create Layout we needed ----------------------------self.Mainlayout = QHBoxLayout() # 主窗口self.Primarylayout = QVBoxLayout() # 功能区布局self.btnlayout = QVBoxLayout() # 功能按钮区布局#---------------- create Layout we needed ----------------------------#---------------- there has more btn but unshowing -------------------# set the convertor buttonsself.UTF8ConvertorBtn = QPushButton("UTF-8 转换") # 创建按钮对象btnReleaseStyleA(self.UTF8ConvertorBtn) # 修改按钮样式,该方法为作者创建,并非第三方库调用self.UTF8ConvertorBtn.clicked.connect(self.Cbtn_press_clicked) # 为按钮连接触发事件,该事件会切换stack# add the buttons to the layoutself.btnlayout.addWidget(self.UTF8ConvertorBtn) # 将按钮添加到功能按钮区布局updateButtonStyle(self,self.UTF8ConvertorBtn) # 更新按钮样式,该方法为作者创建,并非第三方库调用#---------------- there has more btn but unshowing -------------------self.btnlayout.setAlignment(Qt.AlignTop) # 调整按钮区的各组件对齐方式为向上对齐,而非均匀分布#----------------- put the stack in function area --------------------widget = QWidget() # 创建一个widget,用于存放stack布局widget.setLayout(self.stacklayout) # 将stack布局放到widget中self.Primarylayout.addWidget(widget) # 将widget放到功能区布局中,至此stack在功能区中进行切换#----------------- put the stack in function area --------------------#---------------------- create log area ------------------------------self.Consolelayout = QVBoxLayout() # 创建日志区布局self.consoleedit = QTextEdit() # 创建日志编辑文本框self.consoleedit.setReadOnly(True) # 文本框设置为只读TextEditStyle(self.consoleedit) # 修改文本框样式, 该方法为作者创建,并非第三方库调用self.consoleedit.verticalScrollBar().setPageStep(100) # 修改滚动条步长self.Consolelayout.addWidget(self.consoleedit) # 添加文本框到日志区布局中#---------------------- create log area ------------------------------#------------------ put the layouts in main layout -------------------self.Mainlayout.addLayout(self.btnlayout,stretch=1) # 将按钮布局添加到主布局中self.Vline = QFrame(self) # 添加细线 将按钮区与功能区分隔开self.Vline.setFrameShape(QFrame.VLine) # 优化细线self.Vline.setFrameShadow(QFrame.Raised) # 使细线具有凸起的立体感self.Vline.setLineWidth(3) # 设置细线外部粗细self.Vline.setMidLineWidth(1) # 设置细线内部粗细self.Mainlayout.addWidget(self.Vline) # 将细线添加至主布局self.Mainlayout.addLayout(self.Primarylayout,stretch=4) # 将功能区添加至主布局中self.Mainlayout.addLayout(self.Consolelayout,stretch=2) # 将日志区添加至主布局中# add the layout to the windowself.setLayout(self.Mainlayout) # 将主窗口的布局设置为主布局#------------------ put the layouts in main layout -------------------

4.5 结语

到目前为止,我们脚本工具集合包已经具备了初步的大致框架,三大区块已经被划分出来了,接下来的工作将重点放在要开发哪些功能,如何实现这些功能,以及这些功能的UI页面布局。这个系列将会持续更新,动手能力强的小伙伴可以根据路线自己实操一遍,后续我也会将完整代码开源带GitHub上(等系类差不多结束的时候),同时系列文章也会同步更新到我的个人博客中,如果本系列真的帮助到你,请关注本频道,并给我的CSDN 点赞收藏 QAQ,感激不尽!

如有任何疑问,欢迎CSDN私信我或发邮件到707973090@qq.com,在我看到时会第一时间回复!

http://www.lryc.cn/news/474676.html

相关文章:

  • React + Vite + TypeScript + React router项目搭建教程
  • 【ShuQiHere】️ 如何启用 SSH 服务
  • 【自动化测试】APP UI 自动化(安卓)-本地环境搭建
  • java毕业设计之基于Bootstrap的常州地方旅游管理系统的设计与实现(springboot)
  • 《机甲崛起》
  • Windows10:Linux Reader
  • 一、k8s快速入门之学习Kubernetes组件基础
  • PostgreSQL 到 PostgreSQL 数据迁移同步
  • RestTemplate 常用方法(提供了多种方法来发送 HTTP 请求)
  • 常量和变量
  • Go语言的使用
  • 详解CRC校验原理以及FPGA实现
  • 企业如何通过架构蓝图实现数字化转型
  • React第十三章(useTransition)
  • IDEA使用Maven Helper查看整个项目的jar冲突
  • uniapp项目 存储数据到手机本地
  • 景联文科技医疗数据处理平台:强化医疗数据标注与管理,推动医疗数字化新篇章
  • vue使用高德地图实现轨迹显隐
  • Maven(20) 如何使用Maven进行版本管理?
  • AWS RDS MySQL内存使用
  • Vue指令:v-else、v-else-if
  • 基于SSM志愿者招募系统的设计
  • 数学建模与优化算法:从基础理论到实际应用
  • 微信小程序生成二维码
  • 自由软件与开源软件:异同与联系
  • Vue中ref、reactive、toRef、toRefs的区别
  • 凸极式发电机的相量图分析和计算,内功率因数角和外功率因数角和功角的定义。
  • systemctl restart NetworkManager 重启后,文件/etc/resolv.conf修改失败
  • Admin.NET源码学习(5:swagger使用浅析)
  • 在 openEuler 22.03 服务器上搭建 web 服务教程