【PyQt4】学习3 之-串联1、2手撸代码写个界面

本文详细介绍使用PyQt4进行GUI界面布局设计的过程,包括菜单栏、工具栏、文本编辑框、按钮等控件的使用,以及如何通过信号槽机制实现按钮的功能。文章通过一个AI界面设计案例,演示了水平、垂直和网格布局的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本系列文章前情回顾:

  1. PyQt4学习1之---菜单栏(addMenu)、工具栏(addToolBar)、TextEdit工具框
  2. PyQt4学习2之---BoxLayout布局、网格GridLayout布局,实现计算器界面设计

前面的两节中是铺路的,都是为了第三章,这里我们就干一票大事,把前面两节的给整合了,来个AI 的界面初体验,最终的结果如下所示:

一、创建项目

这里不小心就创建了一个QDialog 的项目,其实这个关系不太,下面是这三组项目选择适合的区别,可以在之后的项目中慎重的选择一个,这里就使用QDialog 撰写本项目吧。

Qt 中 QMainWindow、QWidget、QDialog 的区别:

  • QWidget 是所有图形界面的基类,也就是 QMainWindow 和 QDialog 都是 QWidget 的子类;
  • QMainWindow 是一个提供了菜单、工具条的程序主窗口;
  • QDialog 是对话框,多用于短时间与用户的交互。

再回头看一下前面的最终结果图,这里我们就把它想作是UI小姐姐为你精心设计的一个美丽的,富含设计感的UI组合吧,其中包含的主要控件如下:

整理来看,可以分为3大块:

  1. 打印显示输出部分,况且定义为left
  2. 参数输入部分,定义为right
  3. 功能按钮部分,定义为button

(1)打印输出部分,left包含两个

  1. label--打印输出
  2. lineEdit--显示输出信息

(2)参数输入部分,right

  1. 学习率,优化方式等5个label
  2. 其对应的lineEdit,或

(3)三个pushbutton功能按钮

有了这么多,我们接下来就瘦撸代码,实现对上述功能界面的实现吧。

二、动手实现

这里就不一步一步的逐一来实现了,而是先对整个过程进行展示,和章节一类型相似,总览加分述,分述部分我们在第三章节中再进行。话不多说,来看全局代码(代码实现的过程中间进行了各个小段内实现目的的简述,作为对整个代码部分的注释)。

# -*- coding: utf-8 -*-   

from PyQt4 import QtCore
from PyQt4 import QtGui
import sys

QtCore.QTextCodec.setCodecForTr(QtCore.QTextCodec.codecForName("utf8"))

class InputDlg(QtGui.QWidget):
    def __init__(self,parent=None):  
        super(InputDlg,self).__init__(parent)
        QtGui.QWidget.__init__(self,parent=None)
        # self.setGeometry(400,200,640,680)
        self.setWindowTitle(self.tr("自助训练系统"))

        train = QtGui.QPushButton(u'训练开始', self)
        self.connect(train, QtCore.SIGNAL("clicked()"), self.train_slot)    ######################

        test = QtGui.QPushButton(u'测试开始', self)
        self.connect(test, QtCore.SIGNAL("clicked()"), self.main)  #######################

        evaluate = QtGui.QPushButton(u'评估开始', self)
        self.connect(evaluate, QtCore.SIGNAL("clicked()"), self.evaluate_slot)#########################

        # train、test、evaluate水平布局
        bottomLayout = QtGui.QHBoxLayout()
        bottomLayout.addStretch()
        bottomLayout.addWidget(train)
        bottomLayout.addWidget(test)
        bottomLayout.addWidget(evaluate)

        #self.setLayout(bottomLayout)
    ##########################################################################
        label_print = QtGui.QLabel(self.tr("打印输出"))
        self.printTextEdit = QtGui.QTextEdit()

        leftLayout = QtGui.QVBoxLayout()
        leftLayout.addWidget(label_print)
        leftLayout.addWidget(self.printTextEdit)
    ###############################################################################

        label_lr=QtGui.QLabel(self.tr(u"学习率:"))
        label_opt=QtGui.QLabel(self.tr(u"优化方式:"))
        label_me=QtGui.QLabel(self.tr(u"最大迭代数:"))
        label_itr=QtGui.QLabel(self.tr(u"每迭代数保存模型:"))
        label_begin = QtGui.QLabel(self.tr(u"是否从头开始训练:"))

        self.lrLineEdit = QtGui.QLineEdit()

        self.optComboBox = QtGui.QComboBox()
        self.optComboBox.insertItem(0, self.tr("SGD"))
        self.optComboBox.insertItem(1, self.tr("Adam"))

        self.meLineEdit = QtGui.QLineEdit()

        self.itrComboBox = QtGui.QComboBox()
        self.itrComboBox.insertItem(0, self.tr("2"))
        self.itrComboBox.insertItem(1, self.tr("5"))

        self.beginComboBox = QtGui.QComboBox()
        self.beginComboBox.insertItem(0, self.tr("Yes"))
        self.beginComboBox.insertItem(1, self.tr("No"))
        self.checkpointLabel = QtGui.QLabel("0")
        self.connect(self.beginComboBox, QtCore.SIGNAL('currentIndexChanged(int)'), self.retrain_begin_num)

        # 右上侧信息网格布局
        rightLayout = QtGui.QGridLayout()
        rightLayout.addWidget(label_lr,0,0)
        rightLayout.addWidget(self.lrLineEdit,0,1)

        rightLayout.addWidget(label_opt,1,0)
        rightLayout.addWidget(self.optComboBox,1,1)

        rightLayout.addWidget(label_me,2,0)
        rightLayout.addWidget(self.meLineEdit,2,1)

        rightLayout.addWidget(label_itr,3,0)
        rightLayout.addWidget(self.itrComboBox,3,1)

        rightLayout.addWidget(label_begin, 4, 0)
        rightLayout.addWidget(self.beginComboBox, 4, 1)
        rightLayout.addWidget(self.checkpointLabel, 4, 2)
##################################################################
        # 把左部分、右部分、下部分进行网格布局
        mainLayout = QtGui.QGridLayout(self)
        mainLayout.setMargin(15)
        mainLayout.setSpacing(10)
        mainLayout.addLayout(leftLayout, 0, 0)
        mainLayout.addLayout(rightLayout, 0, 1)

        #mainLayout.addLayout(midLayout, 0, 2)
        mainLayout.addLayout(bottomLayout, 1, 0, 1, 2)
        mainLayout.setSizeConstraint(QtGui.QLayout.SetFixedSize)

    ########################################################################
    # Button_runs_slot
    ########################################################################
    def retrain_begin_num(self):
        checkpoint_num, ok = QtGui.QInputDialog.getInteger(self,self.tr("checkpoint_num"),
                                       self.tr("请输入模型开始步数:"),
                                       int(self.checkpointLabel.text()),0,150)
        if ok:
            self.checkpointLabel.setText(str(checkpoint_num))



    def test_slot(self):
        button = QtGui.QMessageBox.question(self, "Question",
                                      self.tr("正在测试,等稍等···?"))

    def evaluate_slot(self):
        button = QtGui.QMessageBox.question(self, "Question",
                                      self.tr("正在评估,等稍等···?"))

    def train_slot(self):
        lr_value = self.lrLineEdit.text()  # 获取文本框内容
        print(u'学习率=', lr_value)

        opt_value = self.optComboBox.currentText()  # 返回选中选项的文本
        print(u'优化方式=', opt_value)

        me_value = self.meLineEdit.text()  # 获取文本框内容
        print(u'最大迭代轮数=', me_value)

        itr_value = self.itrComboBox.currentText()
        print(u'每迭代{}保存模型'.format(itr_value))

        begin_value = self.beginComboBox.currentText()
        print(u'是否从0开始训练=', begin_value)

        checkpoint_value = self.checkpointLabel.text()
        print(u'继续训练,Epoch=', checkpoint_value)

        ###### QtGui.QTextEdit() 显示内容 ##########
        self.printTextEdit.setText(u'打印训练参数:\n' + u'学习率: ' + lr_value
                                   + '\n' + u'优化方式: ' + opt_value
                                   + '\n' + u'最大迭代轮数: ' + me_value
                                   + '\n' + u'每迭代步数保存模型: ' + itr_value
                                   + '\n' + u'是否从0开始训练: ' + begin_value
                                   + '\n' + u'继续训练,Epoch: ' + checkpoint_value)

        button = QtGui.QMessageBox.question(self, u"训练,打印学习率",
                                            self.tr("学习率={}".format(lr_value)),
                                            QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel,
                                            QtGui.QMessageBox.Ok)
        if button == QtGui.QMessageBox.Ok:
            self.tr("ok")
        elif button == QtGui.QMessageBox.Cancel:
            self.tr("bay")
        else:
            return

if __name__ == "__main__":
    app=QtGui.QApplication(sys.argv)

    s=InputDlg()
    s.show()
    app.exec_()

三、代码段详述

顺序,从下往上

(1)从bottom开始吧,控件部分是PushButton,这里节选出来,如下

        self.setWindowTitle(self.tr("自助训练系统"))

        train = QtGui.QPushButton(u'训练开始', self)
        self.connect(train, QtCore.SIGNAL("clicked()"), self.train_slot)    ######################

        test = QtGui.QPushButton(u'测试开始', self)
        self.connect(test, QtCore.SIGNAL("clicked()"), self.main)  #######################

        evaluate = QtGui.QPushButton(u'评估开始', self)
        self.connect(evaluate, QtCore.SIGNAL("clicked()"), self.evaluate_slot)#########################
  1. 首先是对整个GUI界面设置一个窗口标题(setWindowTitle),有一点都是集成self的,在用class类的时候,会比较常见的,在之后的编程中也是要牢记这一点
  2. 添加一个“训练开始”的按钮,在PyQt中,QtGui主要是用于对图像用户接口GUI(Graphical User Interface)中的控件进行定义的,比如按钮、文本框。只要是hi在界面中能够被我妈看到的,都是它来进行定义的;QtCore则于此相反,一个负责貌美如花(人前),一个负责内涵,如3中的信号发射部分
  3. 有了按钮,还需要使得按钮有一定的功能才行。下面这个就是使用才qt中最具代表性的信号槽机制,接下来就看看信号槽机制一个比较形象的图吧(如下)。train---是我们设置的按钮名称;QtCore内有一个信号机制,其中这里的触发是clicked;信号发射后,接受的函数的train-slot函数(这个函数的定义在下文中是需要进行定义的,就是定义我们要这个按钮干点啥)

功能按钮都定义好了,那他们三个究竟会以什么的形式,或者说谁前谁后进行一个排列呢。这里也需要我们自己定义,如下面这样:

        # train、test、evaluate水平布局
        bottomLayout = QtGui.QHBoxLayout()
        bottomLayout.addStretch()
        bottomLayout.addWidget(train)
        bottomLayout.addWidget(test)
        bottomLayout.addWidget(evaluate)
  1. 实例化一个QtGui.QHBoxLayout(),这个是用来水平布局的
  2. 将上文中定义的三个按钮train、test、evaluate使用add添加进去
  3. 按照前面的想法,是不是应该做完啦,那位什么还有一个addStretch语句呢?看下它什么意思,我们翻一下:伸展、拉紧、过分延长,这里和名字的意思一样,是为了使得排列后的格式更加的好看,而故意填充了一块空白。

(2)left定义显示

  1. label--打印输出
  2. lineEdit--显示输出信息

看下,其实也就是这样的思路进行界面的设定的,就不赘述了

但是,有一点是需要进行注意的:常用的方法有addWidget()和addLayout()

  1. addWidget()用于在布局中插入控件
  2. addLayout()用于在布局中插入子布局
        label_print = QtGui.QLabel(self.tr("打印输出"))
        self.printTextEdit = QtGui.QTextEdit()

        leftLayout = QtGui.QVBoxLayout()
        leftLayout.addWidget(label_print)
        leftLayout.addWidget(self.printTextEdit)

添加好后,还是需要进行布局,1中三个是并排放的,水平布局,这里是堆叠放置的,采用垂直布局。先实例化,然后将控件添加进去吧。似不似很简单了呢

(3)第三块部分相对来说看着复杂一些,但仔细一看,其实大体上都是相似的。程序就是这样,看着复杂,其实是有程序化的处理方式的,就显得简单了一些。

代码如下: 

        label_lr=QtGui.QLabel(self.tr(u"学习率:"))
        label_opt=QtGui.QLabel(self.tr(u"优化方式:"))
        label_me=QtGui.QLabel(self.tr(u"最大迭代数:"))
        label_itr=QtGui.QLabel(self.tr(u"每迭代数保存模型:"))
        label_begin = QtGui.QLabel(self.tr(u"是否从头开始训练:"))

        self.lrLineEdit = QtGui.QLineEdit()

        self.optComboBox = QtGui.QComboBox()
        self.optComboBox.insertItem(0, self.tr("SGD"))
        self.optComboBox.insertItem(1, self.tr("Adam"))

        self.meLineEdit = QtGui.QLineEdit()

        self.itrComboBox = QtGui.QComboBox()
        self.itrComboBox.insertItem(0, self.tr("2"))
        self.itrComboBox.insertItem(1, self.tr("5"))

        self.beginComboBox = QtGui.QComboBox()
        self.beginComboBox.insertItem(0, self.tr("Yes"))
        self.beginComboBox.insertItem(1, self.tr("No"))
        self.checkpointLabel = QtGui.QLabel("0")
        self.connect(self.beginComboBox, QtCore.SIGNAL('currentIndexChanged(int)'), self.retrain_begin_num)
  1. 一次性把所有需要的label部分都添加进去
  2. 这种留给用户自己输入的控件,可以就定义为LineEdit,可空可默认;后面还有这种选择性的ComboBox(下拉列表框),设定可选择的东西,这里用插入项目(条款);这块主要是涉及这两个,别的不多说
  3. 有一点不同的地方,就是可以对下拉列表框进设定为信号机制,上面的代码中就设定“当当前的index改变时,就让他发射信号,激活槽函数retrain_begin_num”
  4. 由于right部分,有水平也有垂直方向的,就采用网格形式就行布局吧,(设定参考这里:PyQt4学习2之---BoxLayout布局、网格GridLayout布局,实现计算器界面设计)这如下面这样:
        # 右上侧信息网格布局
        rightLayout = QtGui.QGridLayout()

        # addWidget用于在布局中插入控件
        # QGridLayout.addWidget(窗体, 起始行, 起始列, 占用行, 占用列, 对齐方式)
        rightLayout.addWidget(label_lr,0,0)
        rightLayout.addWidget(self.lrLineEdit,0,1)

        rightLayout.addWidget(label_opt,1,0)
        rightLayout.addWidget(self.optComboBox,1,1)

        rightLayout.addWidget(label_me,2,0)
        rightLayout.addWidget(self.meLineEdit,2,1)

        rightLayout.addWidget(label_itr,3,0)
        rightLayout.addWidget(self.itrComboBox,3,1)

        rightLayout.addWidget(label_begin, 4, 0)
        rightLayout.addWidget(self.beginComboBox, 4, 1)
        rightLayout.addWidget(self.checkpointLabel, 4, 2)

(4)前面三个内容,分别把bottom部分进行了水平布局、把left部分进行了垂直布局、把right部分进行了网格布局,可以说是把qt中比较常用的记住布局方式都囊括了,最后呢,还是要呈现在整个界面上,还是不知道他们三个之间的关系,这里就要把他们三个的关系也明确的定义下,如下:

  • addLayout()用于在布局中插入子布局
        # 把左部分、右部分、下部分进行网格布局
        mainLayout = QtGui.QGridLayout(self)
        mainLayout.setMargin(15) # 表示控件与窗体的左右边距
        mainLayout.setSpacing(10) # 表示各个控件之间的上下间距
        mainLayout.addLayout(leftLayout, 0, 0)
        mainLayout.addLayout(rightLayout, 0, 1)

        #mainLayout.addLayout(midLayout, 0, 2)
        mainLayout.addLayout(bottomLayout, 1, 0, 1, 2)
        mainLayout.setSizeConstraint(QtGui.QLayout.SetFixedSize) # 设置对话框大小固定,不允许用户改变

(5)槽函数实现部分

主要就说下对GUI框中数据的获取吧,然后再打印到显示框中。这个在之后的应用中会比较常见

    def train_slot(self):
        lr_value = self.lrLineEdit.text()  # 获取文本框内容
        print(u'学习率=', lr_value)

        opt_value = self.optComboBox.currentText()  # 返回选中选项的文本
        print(u'优化方式=', opt_value)

        me_value = self.meLineEdit.text()  # 获取文本框内容
        print(u'最大迭代轮数=', me_value)

        itr_value = self.itrComboBox.currentText()
        print(u'每迭代{}保存模型'.format(itr_value))

        begin_value = self.beginComboBox.currentText()
        print(u'是否从0开始训练=', begin_value)

        checkpoint_value = self.checkpointLabel.text()
        print(u'继续训练,Epoch=', checkpoint_value)

        ###### QtGui.QTextEdit() 显示内容 ##########
        self.printTextEdit.setText(u'打印训练参数:\n' + u'学习率: ' + lr_value
                                   + '\n' + u'优化方式: ' + opt_value
                                   + '\n' + u'最大迭代轮数: ' + me_value
                                   + '\n' + u'每迭代步数保存模型: ' + itr_value
                                   + '\n' + u'是否从0开始训练: ' + begin_value
                                   + '\n' + u'继续训练,Epoch: ' + checkpoint_value)

四、总结

qt 关于窗口布局的设计,我知道的有两种方式。

  • 一是直接采用qt 的design进行拖拽式的创建,这样会比较快捷;
  • 另一种就是采用代码的形式,直接进行一个块一个块的设计。

无论是何种方式,最终都会转化为代码的形式,进行转移。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

钱多多先森

你的鼓励,是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值