赞
踩
PyQt 是Python语言的GUI编程解决方案之一,是类似于 Tkinter 的一个高级库。 为了更好的辅助PyQt界面的搭建,可以通过Qt Designer完成GUI界面设计。 使用Qt Designer可以通过拖拽、点击完成GUI界面设计,并且设计完成后生成的.ui程序可以通过 pyuic5 命令直接转换成.py文件以供python程序调用。 搭建完界面并写好逻辑后,还可通过 pyinstaller 将.py文件封装成.exe文件,以供没有python解释器的用户使用。 本文以搭建标注工具界面程序为例。
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
很多控件都可以通过点击(或其他操作)触发事件,事件响应可自由编写,通过 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/" if not os.path.exists(root_path_metadata): os.makedirs(root_path_metadata) class mainWin(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 video def openVideoFile(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() def playPause(self): if self.player.state()==1: self.player.pause() else: self.player.play() def getDuration(self, d): self.sld_duration.setRange(0, d) self.sld_duration.setEnabled(True) self.displayTime(d) def getPosition(self, p): self.sld_duration.setValue(p) self.displayTime(p) def displayTime(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)) def updatePosition(self, v): self.player.setPosition(v) self.displayTime(self.sld_duration.maximum()-v) ## for history def eventFilter(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() return super().eventFilter(widget, event) def completer_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() def completer_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 1 def showPos(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("羡慕") def showNeg(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("纠结") def showNeu(self): self.listWidget.clear() self.listWidget.addItem("共情") self.listWidget.addItem("平静") ## for label 3 def use_subsysdem3(self): if self.checkBox_5.isChecked(): self.frame_9.setHidden(False) else: self.refresh_3() self.frame_9.setHidden(True) def personA(self): self.frame_3.setHidden(False) self.frame_8.setHidden(True) def personB(self): self.frame_3.setHidden(True) self.frame_8.setHidden(False) def refresh_gui(self): self.refresh_1() self.refresh_2() self.refresh_3() self.refresh_save() def refresh_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) def refresh_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("其他") def refresh_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) def refresh_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('') def save_data(self): ## check many things try: self.label_val = self.buttonGroup_2.checkedButton().text() self.label_emotion = self.listWidget.selectedItems()[0].text() except: QMessageBox.information(self,'提示','请选择具体情感后再重新保存', QMessageBox.Yes) return False try: self.label_da = self.listWidget_2.selectedItems()[0].text() except: QMessageBox.information(self,'提示','请选择对话状态后再重新保存', QMessageBox.Yes) return False self.label_iden_isok = self.checkBox_5.isChecked() if self.label_iden_isok: if self.buttonGroup.checkedId() == -1: QMessageBox.information(self,'提示','您已勾选该对话身份可标,请选择说话人身份后再重新保存', QMessageBox.Yes) return False else: 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) return False else: self.label_iden = "不可标" self.label_reason = "不可标" self.label_result = "不可标" self.label_reaction = "不可标" if self.lineEdit_speaker.text() == '': QMessageBox.information(self,'提示','请输入说话人姓名', QMessageBox.Yes) return False else: 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) return False if self.lineEdit.text() == '': QMessageBox.information(self,'提示','请输入视频名字', QMessageBox.Yes) return False else: self.name_dialog = self.lineEdit.text() if not os.path.exists(root_path_metadata+self.name_dialog+'.csv'): with open(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] with open(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)) return True def save_dialog_data(self): flag_save_success = self.save_data() if flag_save_success == False: return 0 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) def del_last_data(self): try: with open(root_path_metadata+self.name_dialog+'.csv',"r",newline='',encoding='utf_8_sig') as csvfile: data = csvfile.readlines() del data[-1] with open(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_())
对于编写好的.py文件,若是需要更好的给没有python编辑器的人使用,则需要封装成.exe文件,这可以通过 pyinstaller 命令来完成。
pyinstaller -F biaozhu.py
pyinstaller 有两种常见的模式:
-F: 將程式打包成单一的执行文件(适合比较简单的代码)
-D: 打包多個文件,exe及依赖的东西会一起放置在dist資料夾里(适合框架形式的程式)
在打包过程中,包含如下步骤
另外,若是打包失败,可以通过改写.spec文件,再通过 pyinstaller -D XXX.spec 重新打包。 如果是库的 import 问题,可以通过 hiddenimport 里放置库名来hidden掉该错误。
以前只知道 Tkinter 可以来实现 python 的界面设计,但感觉并不那么友好。 而这次学习到的 PyQt 以及相应的 Qt designer则很好的解决了这一问题,可以通过拉拽进行布局,就像c++的SDL一样。完成界面设计后的逻辑编写才是更让人头疼的问题,容易产生各种bug,只能慢慢调。 最后完成程序之后还可以转成.exe文件,从而可以直接给别人使用,这个是意料之外的惊喜。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。