0


【界面】使用QT designer、python搭建界面程序

文章目录


前言

PyQt 是Python语言的GUI编程解决方案之一,是类似于 Tkinter 的一个高级库。 为了更好的辅助PyQt界面的搭建,可以通过Qt Designer完成GUI界面设计。 使用Qt Designer可以通过拖拽、点击完成GUI界面设计,并且设计完成后生成的.ui程序可以通过 pyuic5 命令直接转换成.py文件以供python程序调用。 搭建完界面并写好逻辑后,还可通过 pyinstaller 将.py文件封装成.exe文件,以供没有python解释器的用户使用。 本文以搭建标注工具界面程序为例。


1. 预安装的软件与库

  • Qt Designer: pip install —pre pyqt5-tools~=5.11 (位于\Python36\Lib\site-packages\pyqt5_tools\designer.exe,也可通过这里下载)
  • PyQt5: pip install PyQt5
  • pip install pyinstaller

2. Qt Designer 的界面设计

Qt Designer 的界面主要分为四大区:项目区、控件区、编辑区、属性区。 具体而言,就是在【控件区】里点击添加需要的控件,这些控件的效果会在【编辑区】里实时显示,并在【属性区】这些控件的属性,【项目区】用于显示控件间的层级关系。
在这里插入图片描述
在新建一个窗口后,一般需要通过 Container 确定外部轮廓,可选用常见的 Frame 控件,再在 Frame 里边选用 Layouts 来规范后续控件的排列样式,常用水平或垂直排列,最后再选用具体部件往里边填充。 常用的控件有各种Button(按钮)、Label(静态显示文本框)、Text Edit(输入输出文本框)、listWidget(列表显示框)、Check Box(选中框)、各种Slider(滑动条)等。

每一个组件都有可设置的属性,最重要的通用属性有 objectName (用于在后续逻辑编写时指明时哪个控件),text (用于在GUI里在控件上显示), geometry (用于设置控件位置和尺寸,但控件位于Layer中时就不可设置了)。
在这里插入图片描述
设计好界面之后保存可以生成my_win.ui文件,它可以直接在python代码里被加载使用,但为了在代码里进一步调用修改等,更好的方法是将.ui文件转换成相应的.py文件。这需要借助 \Python36\Scripts\pyuic5.exe工具。

pyuic5 -o my_win.py my_win.ui

3. Qt 逻辑编写

很多控件都可以通过点击(或其他操作)触发事件,事件响应可自由编写,通过 connect 函数绑定。

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""
Created on Sun June 12 12:06:21 2020

@author: weiquan fan
"""import sys, os
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QCompleter, QFileDialog
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtCore import pyqtSignal, QEvent
from PyQt5.Qt import QUrl
from my_win import Ui_MainWindow
import csv

root_path_metadata ="./data/"ifnot os.path.exists(root_path_metadata):
    os.makedirs(root_path_metadata)classmainWin(QMainWindow, Ui_MainWindow):
    doubleClicked_speaker = pyqtSignal()
    doubleClicked_dialog = pyqtSignal()def__init__(self, parent=None):super(mainWin, self).__init__(parent)
        self.setupUi(self)## emotion
        self.refresh_1()
        self.radioButton.clicked.connect(self.showPos)
        self.radioButton_2.clicked.connect(self.showNeu)
        self.radioButton_3.clicked.connect(self.showNeg)## DA
        self.refresh_2()## dialog identity
        self.refresh_3()
        self.frame_9.setHidden(True)
        self.checkBox_5.stateChanged.connect(self.use_subsysdem3)
        self.radioButton_5.clicked.connect(self.personA)
        self.radioButton_4.clicked.connect(self.personB)## save buttons
        self.refresh_save()
        self.btn_save.clicked.connect(self.save_data)
        self.btn_save_2.clicked.connect(self.save_dialog_data)## history
        self.list_speaker =[]
        self.list_dialog =[]

        self.lineEdit_speaker.installEventFilter(self)
        self.lineEdit.installEventFilter(self)
        self.doubleClicked_speaker.connect(self.completer_name_speaker)
        self.doubleClicked_dialog.connect(self.completer_name_dialog)## video player
        self.player = QMediaPlayer()
        self.player.setVideoOutput(self.wgt_player)
        self.btn_open.clicked.connect(self.openVideoFile)    
        self.btn_play_pause.clicked.connect(self.playPause)    
        self.player.durationChanged.connect(self.getDuration)
        self.player.positionChanged.connect(self.getPosition)
        self.sld_duration.sliderMoved.connect(self.updatePosition)## for opening videodefopenVideoFile(self):
        name = QFileDialog.getOpenFileName()[0]
        self.lineEdit.setText(name.split('/')[-1])
        self.player.setMedia(QMediaContent(QUrl.fromLocalFile(name)))# self.player.setMedia(QMediaContent(QFileDialog.getOpenFileUrl()[0]))
        self.player.play()defplayPause(self):if self.player.state()==1:
            self.player.pause()else:
            self.player.play()defgetDuration(self, d):
        self.sld_duration.setRange(0, d)
        self.sld_duration.setEnabled(True)
        self.displayTime(d)defgetPosition(self, p):
        self.sld_duration.setValue(p)
        self.displayTime(p)defdisplayTime(self, ms):
        minutes =int(ms/60000)
        seconds =int((ms-minutes*60000)/1000)
        dur_ms = self.sld_duration.maximum()
        dur_min =int(dur_ms/60000)
        dur_sec =int((dur_ms-dur_min*60000)/1000)
        self.lab_duration.setText('{:0>2d}:{:0>2d} / {:0>2d}:{:0>2d}'.format(minutes, seconds, dur_min, dur_sec))defupdatePosition(self, v):
        self.player.setPosition(v)
        self.displayTime(self.sld_duration.maximum()-v)## for historydefeventFilter(self, widget, event):if widget == self.lineEdit_speaker:if event.type()== QEvent.MouseButtonDblClick:
                self.doubleClicked_speaker.emit()elif widget == self.lineEdit:if event.type()== QEvent.MouseButtonDblClick:
                self.doubleClicked_dialog.emit()returnsuper().eventFilter(widget, event)defcompleter_name_dialog(self):
        self.completer = QCompleter(self.list_dialog)
        self.lineEdit.setCompleter(self.completer)
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.completer.complete()
        self.completer.popup()defcompleter_name_speaker(self):
        self.completer = QCompleter(self.list_speaker)
        self.lineEdit_speaker.setCompleter(self.completer)
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.completer.complete()
        self.completer.popup()## for label 1defshowPos(self):
        self.listWidget.clear()
        self.listWidget.addItem("高兴")
        self.listWidget.addItem("兴奋")
        self.listWidget.addItem("自豪")
        self.listWidget.addItem("满足")
        self.listWidget.addItem("感激")
        self.listWidget.addItem("自信")
        self.listWidget.addItem("轻松")
        self.listWidget.addItem("羡慕")defshowNeg(self):
        self.listWidget.clear()
        self.listWidget.addItem("生气")
        self.listWidget.addItem("伤心")
        self.listWidget.addItem("害怕")
        self.listWidget.addItem("烦恼")
        self.listWidget.addItem("孤独")
        self.listWidget.addItem("羞愧")
        self.listWidget.addItem("恶心")
        self.listWidget.addItem("失望")
        self.listWidget.addItem("郁闷")
        self.listWidget.addItem("不安")
        self.listWidget.addItem("紧张")
        self.listWidget.addItem("无奈")
        self.listWidget.addItem("纠结")defshowNeu(self):
        self.listWidget.clear()
        self.listWidget.addItem("共情")
        self.listWidget.addItem("平静")## for label 3defuse_subsysdem3(self):if self.checkBox_5.isChecked():
            self.frame_9.setHidden(False)else:
            self.refresh_3()
            self.frame_9.setHidden(True)defpersonA(self):
        self.frame_3.setHidden(False)
        self.frame_8.setHidden(True)defpersonB(self):
        self.frame_3.setHidden(True)        
        self.frame_8.setHidden(False)defrefresh_gui(self):
        self.refresh_1()
        self.refresh_2()
        self.refresh_3()
        self.refresh_save()defrefresh_1(self):
        self.buttonGroup_2.setExclusive(False)
        self.radioButton.setChecked(False)
        self.radioButton_2.setChecked(False)
        self.radioButton_3.setChecked(False)
        self.buttonGroup_2.setExclusive(True)
        self.listWidget.clear()
        self.checkBox_3.setChecked(True)
        self.checkBox_2.setChecked(True)
        self.checkBox.setChecked(True)        
        self.checkBox_4.setChecked(False)defrefresh_2(self):
        self.listWidget_2.clear()
        self.listWidget_2.addItem("问候")
        self.listWidget_2.addItem("提问")
        self.listWidget_2.addItem("回答")
        self.listWidget_2.addItem("陈述观点")
        self.listWidget_2.addItem("陈述非观点")
        self.listWidget_2.addItem("道歉")
        self.listWidget_2.addItem("命令")
        self.listWidget_2.addItem("赞同")
        self.listWidget_2.addItem("反对")
        self.listWidget_2.addItem("表达知会")
        self.listWidget_2.addItem("欣赏")
        self.listWidget_2.addItem("叹词")
        self.listWidget_2.addItem("结束对话") 
        self.listWidget_2.addItem("引用") 
        self.listWidget_2.addItem("其他")defrefresh_3(self):# self.checkBox_5.setChecked(False)
        self.buttonGroup.setExclusive(False)
        self.radioButton_4.setChecked(False)
        self.radioButton_5.setChecked(False)
        self.buttonGroup.setExclusive(True)
        self.buttonGroup_3.setExclusive(False)
        self.radioButton_6.setChecked(False)
        self.radioButton_7.setChecked(False)
        self.radioButton_8.setChecked(False)
        self.radioButton_9.setChecked(False)        
        self.radioButton_10.setChecked(False)        
        self.buttonGroup_3.setExclusive(True)# self.frame_9.setHidden(True)
        self.frame_3.setHidden(True)
        self.frame_8.setHidden(True)defrefresh_save(self):
        self.lineEdit_2.setText('0')
        self.lineEdit_3.setText('0')
        self.lineEdit_4.setText('0')
        self.lineEdit_5.setText('0')
        self.lineEdit_6.setText('0')
        self.lineEdit_7.setText('0')
        self.lineEdit_speaker.setText('')defsave_data(self):## check many thingstry:
            self.label_val = self.buttonGroup_2.checkedButton().text()
            self.label_emotion = self.listWidget.selectedItems()[0].text()except:
            QMessageBox.information(self,'提示','请选择具体情感后再重新保存', QMessageBox.Yes)returnFalsetry:
            self.label_da = self.listWidget_2.selectedItems()[0].text()except:
            QMessageBox.information(self,'提示','请选择对话状态后再重新保存', QMessageBox.Yes)returnFalse

        self.label_iden_isok = self.checkBox_5.isChecked()if self.label_iden_isok:if self.buttonGroup.checkedId()==-1:
                QMessageBox.information(self,'提示','您已勾选该对话身份可标,请选择说话人身份后再重新保存', QMessageBox.Yes)returnFalseelse:
                self.label_iden = self.buttonGroup.checkedButton().text()if self.label_iden =="倾诉者":
                    self.label_reason = self.lineEdit_reason.text()
                    self.label_result = self.lineEdit_result.text()
                    self.label_reaction ="空"else:
                    self.label_reason ="空"
                    self.label_result ="空"try:
                        self.label_reaction = self.buttonGroup_3.checkedButton().text()except:
                        QMessageBox.information(self,'提示','您已勾选该对话身份可标,请选择倾诉者反应后再重新保存', QMessageBox.Yes)returnFalseelse:
            self.label_iden ="不可标"
            self.label_reason ="不可标"
            self.label_result ="不可标"
            self.label_reaction ="不可标"if self.lineEdit_speaker.text()=='':
            QMessageBox.information(self,'提示','请输入说话人姓名', QMessageBox.Yes)returnFalseelse:
            self.name_speaker = self.lineEdit_speaker.text()try:
            self.start_time ="{}:{}:{}".format(int(self.lineEdit_2.text()),int(self.lineEdit_3.text()),int(self.lineEdit_4.text()))
            self.end_time ="{}:{}:{}".format(int(self.lineEdit_5.text()),int(self.lineEdit_6.text()),int(self.lineEdit_7.text()))except:
            QMessageBox.information(self,'提示','时间应输入整数', QMessageBox.Yes)returnFalseif self.lineEdit.text()=='':
            QMessageBox.information(self,'提示','请输入视频名字', QMessageBox.Yes)returnFalseelse:
            self.name_dialog = self.lineEdit.text()ifnot os.path.exists(root_path_metadata+self.name_dialog+'.csv'):withopen(root_path_metadata+self.name_dialog+'.csv',"a",newline='',encoding='utf_8_sig')as csvfile: 
                writer = csv.writer(csvfile, delimiter=',')
                writer.writerow(['视频名字','说话者姓名','起始时间','结束时间','情绪(粗粒度)','情绪(细粒度)','是否基于音频','是否基于视频','是否基于文本','是否难以标注','对话状态','是否可标对话身份','说话人身份','起因','结果','倾诉者反应'])## save
        self.label_emotion_audio_based = self.checkBox_3.isChecked()
        self.label_emotion_video_based = self.checkBox_2.isChecked()
        self.label_emotion_text_based = self.checkBox.isChecked()
        self.label_emotion_hard = self.checkBox_4.isChecked()

        onelist =[self.name_dialog, self.name_speaker, self.start_time, self.end_time, self.label_val, self.label_emotion, self.label_emotion_audio_based, self.label_emotion_video_based, self.label_emotion_text_based, self.label_emotion_hard, self.label_da, self.label_iden_isok, self.label_iden, self.label_reason, self.label_result, self.label_reaction]withopen(root_path_metadata+self.name_dialog+'.csv',"a",newline='',encoding='utf_8_sig')as csvfile: 
            writer = csv.writer(csvfile, delimiter=',')
            writer.writerow(onelist)

        self.refresh_gui()
        self.list_dialog.append(self.name_dialog)
        self.list_speaker.append(self.name_speaker)
        self.list_dialog =list(set(self.list_dialog))
        self.list_speaker =list(set(self.list_speaker))# self.lineEdit.setCompleter(QCompleter(self.list_dialog))# self.lineEdit_speaker.setCompleter(QCompleter(self.list_speaker))returnTruedefsave_dialog_data(self):
        flag_save_success = self.save_data()if flag_save_success ==False:return0
        QMessageBox.about(self,'提示','对话保存成功')
        self.refresh_gui()
        self.lineEdit.setText('')
        self.lineEdit_reason.setText('')
        self.lineEdit_result.setText('')
        self.checkBox_5.setChecked(False)
        self.frame_9.setHidden(True)defdel_last_data(self):try:withopen(root_path_metadata+self.name_dialog+'.csv',"r",newline='',encoding='utf_8_sig')as csvfile: 
                data = csvfile.readlines()del data[-1]withopen(root_path_metadata+self.name_dialog+'.csv',"w",newline='',encoding='utf_8_sig')as csvfile: 
                writer = csv.writer(csvfile, delimiter=',')for row in data:
                    writer.writerow(row.strip().split(','))# writer.writerows(data)
            QMessageBox.about(self,'提示','上一句的标注已删除')except:
            QMessageBox.information(self,'提示','该视频尚未保存任何数据', QMessageBox.Yes)if __name__ =='__main__':
    app = QApplication(sys.argv)
    main_win = mainWin()
    main_win.show()# main_win.showFullScreen()
    sys.exit(app.exec_())

4. 封装成可执行文件

对于编写好的.py文件,若是需要更好的给没有python编辑器的人使用,则需要封装成.exe文件,这可以通过 pyinstaller 命令来完成。

pyinstaller -F biaozhu.py

pyinstaller 有两种常见的模式:
-F: 將程式打包成单一的执行文件(适合比较简单的代码)
-D: 打包多個文件,exe及依赖的东西会一起放置在dist資料夾里(适合框架形式的程式)

在打包过程中,包含如下步骤

  • 在路径下生成了biaozhu.spec: 包含打包时相关的设定
  • 建立build 文件夹,放置了log记录和相关文件
  • 建立dist 文件夹,放置了可执行文件,若是 —F 模式,则里边仅有一个.exe文件

另外,若是打包失败,可以通过改写.spec文件,再通过 pyinstaller -D XXX.spec 重新打包。 如果是库的 import 问题,可以通过 hiddenimport 里放置库名来hidden掉该错误。

5. 总结

以前只知道 Tkinter 可以来实现 python 的界面设计,但感觉并不那么友好。 而这次学习到的 PyQt 以及相应的 Qt designer则很好的解决了这一问题,可以通过拉拽进行布局,就像c++的SDL一样。完成界面设计后的逻辑编写才是更让人头疼的问题,容易产生各种bug,只能慢慢调。 最后完成程序之后还可以转成.exe文件,从而可以直接给别人使用,这个是意料之外的惊喜。

标签: qt python ui

本文转载自: https://blog.csdn.net/tobefans/article/details/125433657
版权归原作者 weiquan fan 所有, 如有侵权,请联系我们删除。

“【界面】使用QT designer、python搭建界面程序”的评论:

还没有评论