学习pyqt (二)————信号/槽&主次界面&主次线程编程经验总结

今天准备总结一下pyqt编写界面的经验,这样以后遇到同样问题就可以查看并且很快回忆起来!

环境搭建

经过一周的研究,我发现使用pyqt编写界面最快的方式还是eric + pycharm

使用eric6pycharm同时打开你正在编写的程序,需要界面编写就在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()

    # 定义一个名叫 'rangeChanged' 的信号,包含两个int类型的参数
    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, '关于本软件', '本软件版权属于……')

自定义对话框窗口

这里一定要使用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)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现Word文件的导入上传功能,可以使用PyQt5中的QFileDialog和QTextEdit组件。具体步骤如下: 1. 导入PyQt5和Python-docx库 ``` from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QTextEdit import docx ``` 2. 创建一个主窗口,并添加一个文本编辑框 ``` class MainWindow(QMainWindow): def __init__(self): super().__init__() self.text_edit = QTextEdit() self.setCentralWidget(self.text_edit) ``` 3. 添加一个菜单,用于选择Word文件并将其导入到文本编辑框中 ``` class MainWindow(QMainWindow): def __init__(self): super().__init__() self.text_edit = QTextEdit() self.setCentralWidget(self.text_edit) file_menu = self.menuBar().addMenu(&quot;File&quot;) open_file_action = file_menu.addAction(&quot;Open File&quot;) open_file_action.triggered.connect(self.open_file) def open_file(self): file_name, _ = QFileDialog.getOpenFileName(self, &quot;Open Word File&quot;, &quot;&quot;, &quot;Word Files (*.docx)&quot;) if file_name: doc = docx.Document(file_name) text = &quot;\n&quot;.join([paragraph.text for paragraph in doc.paragraphs]) self.text_edit.setText(text) ``` 这样,当用户选择&ldquo;Open File&rdquo;菜单时,会打开一个文件选择对话框,用户选择Word文件后,程序会将其内容读取并显示在文本编辑框中。 4. 添加一个菜单,用于将文本编辑框中的内容导出为Word文件 ``` class MainWindow(QMainWindow): def __init__(self): super().__init__() self.text_edit = QTextEdit() self.setCentralWidget(self.text_edit) file_menu = self.menuBar().addMenu(&quot;File&quot;) open_file_action = file_menu.addAction(&quot;Open File&quot;) open_file_action.triggered.connect(self.open_file) save_file_action = file_menu.addAction(&quot;Save File&quot;) save_file_action.triggered.connect(self.save_file) def open_file(self): file_name, _ = QFileDialog.getOpenFileName(self, &quot;Open Word File&quot;, &quot;&quot;, &quot;Word Files (*.docx)&quot;) if file_name: doc = docx.Document(file_name) text = &quot;\n&quot;.join([paragraph.text for paragraph in doc.paragraphs]) self.text_edit.setText(text) def save_file(self): file_name, _ = QFileDialog.getSaveFileName(self, &quot;Save Word File&quot;, &quot;&quot;, &quot;Word Files (*.docx)&quot;) if file_name: doc = docx.Document() for line in self.text_edit.toPlainText().split(&quot;\n&quot;): doc.add_paragraph(line) doc.save(file_name) ``` 这样,当用户选择&ldquo;Save File&rdquo;菜单时,会打开一个文件保存对话框,用户选择保存的文件名后,程序会将文本编辑框中的内容保存为Word文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值