Skip to content
约 1 分钟阅读阅读

基于PyQt开发的脚本集合包(四)

PyQt主窗口框架设计(QWidget)

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

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

4.1 import部分

python
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 重定向操作台输出

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

python
        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方法:

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

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

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

python
  	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初始化

python
    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 buttons
        self.UTF8ConvertorBtn = QPushButton("UTF-8 转换") # 创建按钮对象
        btnReleaseStyleA(self.UTF8ConvertorBtn) # 修改按钮样式,该方法为作者创建,并非第三方库调用
        self.UTF8ConvertorBtn.clicked.connect(self.Cbtn_press_clicked) # 为按钮连接触发事件,该事件会切换stack
        # add the buttons to the layout
        self.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 window
        self.setLayout(self.Mainlayout) # 将主窗口的布局设置为主布局
        #------------------ put the layouts in main layout -------------------

4.5 结语

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

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