今天是周日,经过昨天一夜的大雨,早晨的天气真的非常清爽!今天准备思考一下论文对环境实际的意义,然后顺便
继续总结上周使用pyqt编程的经验。这样以后遇到同样的问题就能很快回忆起来啦~
今天准备总结一下pyqt编写界面的经验,这样以后遇到同样问题就可以查看并且很快回忆起来!
环境搭建
经过一周的研究,我发现使用pyqt编写界面最快的方式还是eric + pycharm
使用eric6
和pycharm
同时打开你正在编写的程序,需要界面编写就在eric6
里面完成,需要调试并且找到某个变量的类型
时,就在pycharm
里面设置断点,然后进行断点调试。
主界面绘制经验
主界面绘制方法:
- 打开左边
Project-Viewer
栏里面的Form
标签 - 右键选择
New Form...
里面的Main Window
- 在弹出的
Qt Design
里面进行绘制即可
信号和槽的使用经验
如果想要在python
中使用信号和槽函数,需要要求使用的类继承QObjcet
。然后使用PyQt5.QtCore.pyqtSignal
函数新建一个信号,
比如:
from PyQt5.QtCore import QObject, pyqtSignal
class Foo(QObject):
# 定义一个无参数的信号'closed'。
closed = pyqtSignal()
# This defines a signal called 'rangeChanged' that takes two
# integer arguments.
range_changed = pyqtSignal(int, int, name='rangeChanged')
# This defines a signal called 'valueChanged' that has two overloads,
# one that takes an integer argument and one that takes a QString
# argument. Note that because we use a string to specify the type of
# the QString argument then this code will run under Python v2 and v3.
valueChanged = pyqtSignal([int], ['QString'])
定义了信号之后,就可以使用该信号的connect
方法连接槽,或者使用emit
方法发送信号,比如:
from PyQt5.QtCore import QObject, pyqtSignal
class Foo(QObject):
# 新建一个无参数的信号
trigger = pyqtSignal()
def connect_and_emit_trigger(self):
# 连接槽。
self.trigger.connect(self.handle_trigger)
# 发射信号。
self.trigger.emit()
def handle_trigger(self):
# 打印该槽调用成功的消息。
print "trigger signal received"
主界面和次级界面编程经验
我实际编写界面程序时的原则是:如果不需要次级窗口,就尽量不用,因为每多一个次级窗口,就代表要多两个.py
文件。
但是当界面逻辑很多时,如果不用次级窗口,就会导致主窗口的槽函数非常多,看起来不方便。所以到底是否需要用次级窗口。
还是要根据你自己的习惯选择比较好。这里仅仅介绍如何实现次级窗口
Qt内置次级窗口
Qt内置次级窗口比较简单,只需要直接调用函数即可,比如保存对话框:
saveStrList, classify = QtWidgets.QFileDialog.getSaveFileName(self, '选择保存文件', os.path.join(self.FileDirStr, '检测出的异常类型'), 'text(*.txt)')
还有消息提示对话框:
QtWidgets.QMessageBox.about(self, '关于本软件', '本软件版权属于CNIC第314房间课题组~')
自定义对话框窗口
这里一定要使用QDialog
,因为我发现使用QWidget
就没有关闭按钮,不知道为啥。但是Qdialog
继续Qwidget
,因此
使用它肯定能完成Qwidget
的任务。
前两步跟编写主界面没区别,在Forms
区域新建Qdialog
,命名,然后打开QtDesign
,绘制自己需要的控件,然后进行绘制即可.
然后在主界面中需要打开该对话框的位置输入代码:
theSplitWidget = splitDialog() #splitDialog是我自定义的一个次级窗口
theSplitWidget.exec_()
主线程和次线程编程经验
在计算复杂任务的时候,为了保证主界面不失去响应,就必须要使用多线程编程。通常情况下,需要让主线程的界面上出现一些 进度条或者状态栏提示,让客户可以观察到复杂任务执行进度,这样才能让客户知道程序仍然在正常运行和执行的进度,决定是否 继续运行该复杂任务。
自己实现线程,需要继承PyQt5.QtCore.QThread
类(一开始我继承(QObjcet,Thread.threading,发现有问题,最后想到还不如直接继承
Qt自带的线程类)。
这样可以自定义信号,因为主线程和次线程的通信要使用信号和槽函数,因为Qt
的信号好槽是可以跨线程的,而子线程不能用于直接刷新界面。
如果直接自己定义信号和槽函数,按照“信号和槽的使用经验”里面讲的方式即可,这里创建的线程直接的通信只涉及基本数据结构,这样就不用在Qt中注册元信息了。
思考一下,如果直接主线程逻辑中新建定义一个线程,这样无法完成通信操作
对于复杂到数据结构的通信,我就只让子线程执行完毕后,将结果通过线程的一个函数getResult
返回即可。
详细代码如下:
from PyQt5.QtCore import pyqtSignal,QThread
class theThread(QThread):
def __init__(self):
super(theThread, self).__init__()
def setFun(self,fun):
self.func = fun
def setArgs(self,args):
self.args = args
self.FileDirStr = args[3]
def getResult(self):
return self.res
def run(self):
print('starting thread', self.name)
self.res = self.func(self,self.args[0], self.args[1], self.args[2])
print('finished thread', self.name)
progress_changed = pyqtSignal(int, name='progressChanged')
textBrower_changed = pyqtSignal(str, name='textBrowerChanged')
status_changed = pyqtSignal(str, name='statusChanged')
autoCheckBox_changed = pyqtSignal(bool, name='autoCheckBoxChanged')
HeightLineEdit_changed = pyqtSignal(str, name='HeightLineEditChanged')
这样可以看出,在run函数中调用了该线程在设置setFun
时的函数。可以看出,线程调用的需要复杂计算量的函数,最好使用
**kw
关键字参数。因为在设置参数setArgs
函数时必须确定传入的参数的数目,用一个关键字参数就可以解决很多问题。
同时函数中需要传入线程本身self
的参数,需要更新界面状态就通过线程本身发射信号,这样主线程中开始该线程时要连接
对应信号即可。
必然在主线程中连接信号和槽函数的代码可以这样实现:
def __init__(self, parent=None):
"""
Constructor
@param parent reference to the parent widget
@type QWidget
"""
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.thread = theThread.theThread()
self.thread.progress_changed.connect(self.progressBar_gen.setValue) #连接Qt的进度条自带的槽函数
self.thread.textBrower_changed.connect(self.textBrowser_message.append) #连接Qt的文本浏览器自带的槽函数
self.thread.status_changed.connect(self.statusBar.showMessage) #连接Qt的状态栏自带的槽函数
self.thread.autoCheckBox_changed.connect(self.checkBox_auto.setChecked) #连接Qt的选择框自带的槽函数
self.thread.HeightLineEdit_changed.connect(self.lineEdit_height.setText) #连接Qt的行编辑自带的槽函数
self.thread.finished.connect(self.threadFinished) #使用Qt的`Qthread`自带信号连接自定义的槽函数,后面需要实现threadFinished方法!!
注意:前五个槽连接的是Qt对应控件自带的槽函数,最后一个连接的是自定义槽,所以我后面实现了该槽的内容:
def threadFinished(self):
try:
IPReg = '(?<![\.\d])(?:25[0-5]\.|2[0-4]\d\.|[01]?\d\d?\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)(?![\.\d])'
(patternLog_new, patternLog_old, modeClassDF, ParseDF, AnomalyList, timeMap, resultToShow) = self.thread.getResult()
self.progressBar_gen.setValue(50)
IPCompile = src.analysisAnomalyModeResult.getCompile(IPReg)
result = dict()
for typei in range(len(modeClassDF)):
inputDf = modeClassDF[typei]
inputDf.index = [i for i in range(inputDf.shape[0])] #重排列Index,否则后面使用时会出错
test = src.analysisAnomalyModeResult.dealOneAnomalyDF(inputDf,IPCompile)
result['type'+str(typei)] = test
self.progressBar_gen.setValue(75)
fileNameForSaveResult = os.path.join(self.FileDirStr,'异常模式结果')
src.analysisAnomalyModeResult.saveResult(fileNameForSaveResult,result)
self.progressBar_gen.setValue(100)
self.end = time.clock()
self.statusBar.showMessage('生成异常模式完成!一共花费了%.2f秒' % (self.end-self.start)) # 状态栏提示信息
#记录时间结果显示模块
totalTime = self.end - self.start
parseLogTime = timeMap['parseLogTime'] - self.start
vectorTime = timeMap['vectorTime'] - timeMap['parseLogTime']
logListTime = timeMap['logListTime'] - timeMap['vectorTime']
filterTime = timeMap['filterTime'] - timeMap['logListTime']
distanceTime = timeMap['distanceTime'] - timeMap['filterTime']
clusterTime = timeMap['clusterTime'] - timeMap['distanceTime']
self.textBrowser_message.append('\n生成异常模式完成!一共花费了%.2f秒:其中解析日志花费时间%.2f秒,占比%.2f%%;计算日志向量花费时间%.2f秒,占比%.2f%%; \
计算日志列表和日志模式花费时间%.2f秒,占比%.2f%%;异常流量筛选花费时间%.2f秒,占比%.2f%%;计算距离矩阵花费时间%.2f秒,占比%.2f%%;进行层次聚类花费时间%.2f秒,占比%.2f%%。'
% (totalTime, parseLogTime, parseLogTime/totalTime*100, vectorTime, vectorTime/totalTime*100, logListTime, logListTime/totalTime*100, filterTime, filterTime/totalTime*100,
distanceTime, distanceTime/totalTime*100,clusterTime, clusterTime/totalTime*100,))
QtWidgets.QMessageBox.information(self, '提醒', '生成异常模式完成!')
except Exception as e:
print(e)
print(timeMap)
self.statusBar.showMessage('生成异常模式失败!') # 状态栏提示信息
self.progressBar_gen.setValue(0)