pyqt5信号处理模块——signal

Python中的信号处理模块——signal

def ctrlCHandler(sig, frame):
    print("CTRL-C Handler:")
    print("Trackback:")
    faulthandler.dump_traceback()
    print("")
    print("Aborting now")
    assert(False)

if (__name__ == "__main__"):

    # Use both so that we can pass sys.argv to QApplication.
    import argparse
    import sys

    # Get command line arguments..
    parser = argparse.ArgumentParser(description = 'STORM microscope control software')
    parser.add_argument('config', type = str, help = "The name of the configuration file to use.")
    parser.add_argument('--xml', dest = 'default_xml', type = str, required = False, default = None,
                        help = "The name of a settings xml file to use as the default.")

    args = parser.parse_args()
    
    # Start..
    app = QtWidgets.QApplication(sys.argv)

    # This keeps Qt from closing everything if a message box is displayed
    # before HAL's main window is shown.
    app.setQuitOnLastWindowClosed(False)

    # Splash Screen.
    pixmap = QtGui.QPixmap("splash.png")
    splash = QtWidgets.QSplashScreen(pixmap)
    splash.show()
    app.processEvents()

    # Load configuration.
    config = params.config(args.config)

    # Start logger.
    hdebug.startLogging(config.get("directory") + "logs/", "hal4000")
    
    # Setup HAL and all of the modules.
    hal = HalCore(config = config,
                  parameters_file_name = args.default_xml)
    #主程序,配置初始文件,也能传递数据和结束进程

    # Hide splash screen and start.
    splash.hide()

    # Configure ctrl-c handling.
    signal.signal(signal.SIGINT, ctrlCHandler)
    #SIGINT抓住crtl+c命令,故命令行窗口 crtl+c ,退出应用
    
    app.exec_()

HalCore(config = config, parameters_file_name = args.default_xml) 函数:
The core of it all. It sets everything else up, handles the message passing and tears everything down.
config是从xml文件中解析出的,

class HalCore(QtCore.QObject):
    """
    The core of it all. It sets everything else up, handles 
    the message passing and tears everything down.
    """
    def __init__(self, config = None,
                 parameters_file_name = None,
                 testing_mode = False,
                 show_gui = True,
                 **kwds):
        super().__init__(**kwds)

        self.modules = []
        self.module_name = "core"
        self.qt_settings = QtCore.QSettings("storm-control", "hal4000" + config.get("setup_name").lower())
        self.queued_messages = deque()   #双端队列
        self.queued_messages_timer = QtCore.QTimer(self)
        self.running = True # This is solely for the benefit of unit tests.
        self.sent_messages = []
        self.strict = config.get("strict", False)

        self.queued_messages_timer.setInterval(0)
        self.queued_messages_timer.timeout.connect(self.handleSendMessage)
        self.queued_messages_timer.setSingleShot(True)

        # Initialize messages.
        halMessage.initializeMessages()

        # In strict mode we all workers must finish in 60 seconds.
        if self.strict:
            halModule.max_job_time = 60000
            
        # Load all the modules.
        print("Loading modules")

        #
        # For HAL it is easier to just use a list of modules, but at initialization
        # we also send a dictionary with the module names as keys to all of the
        # modules
        #
        # In testing mode the testing.testing module may use the other modules to
        # spoof the message sources.
        #
        # During normal operation most inter-module communication is done using
        # messages. Modules may also request functionalities from other modules
        # that they can use to do specific tasks, such as daq output or displaying
        # the images from a camera.
        #
        all_modules = {}
        if testing_mode:
            all_modules["core"] = self
        else:
            all_modules["core"] = True

        #
        # Need to load HAL's main window first so that other GUI windows will
        # have the correct Qt parent.
        #
        module_names = sorted(config.get("modules").getAttrs())
        module_names.insert(0, module_names.pop(module_names.index("hal")))        
        for module_name in module_names:
            print("  " + module_name)

            # Get module specific parameters.
            module_params = config.get("modules").get(module_name)

            # Add the 'root' parameters to this module parameters
            # so that they are visible to the module.
            for root_param in config.getAttrs():
                if (root_param != "modules"):
                    module_params.add(root_param, config.getp(root_param))

            # Load the module.
            a_module = importlib.import_module(module_params.get("module_name"))
            a_class = getattr(a_module, module_params.get("class_name"))
            a_object = a_class(module_name = module_name,
                               module_params = module_params,
                               qt_settings = self.qt_settings)

            # If this is HAL's main window set the HalDialog qt_parent class
            # attribute so that any GUI QDialogs will have the correct Qt parent.
            if (module_name == "hal"):
                halDialog.HalDialog.qt_parent = a_object.view
                
            self.modules.append(a_object)
            if testing_mode:
                all_modules[module_name] = a_object
            else:
                all_modules[module_name] = True

        print("")

        # Connect signals.
        for module in self.modules:
            module.newMessage.connect(self.handleMessage)

        # Create messages.
        #
        # We do it this way with finalizers because otherwise all of these messages
        # would get queued first and the modules would not have a chance to insert
        # messages in between these messages.
        #
        # The actual sequence of sent messages is:
        #
        # 1. "configure1", tell modules to finish configuration.
        #    The message includes a dictionary of the names of
        #    all modules that were loaded.
        #
        # 2. "configure2", gives the modules a chance to 'react'
        #    based on what happened during configure1.
        #
        # 3. "configure3", gives the modules a chance to 'react'
        #    based on what happened during configure2.
        #
        # 4. "new parameters file", initial parameters (if any).
        #
        # 5. "start", tell the modules to start.
        #    This is the point where any GUI modules that are
        #    visible should call show().
        #
        message_chain = []

        # configure1.
        message_chain.append(halMessage.HalMessage(source = self,
                                                   m_type = "configure1",
                                                   data = {"all_modules" : all_modules}))

        # configure2.
        message_chain.append(halMessage.HalMessage(source = self,
                                                   m_type = "configure2"))

        # configure3.
        message_chain.append(halMessage.HalMessage(source = self,
                                                   m_type = "configure3"))
        
        # update default parameters.
        if parameters_file_name is not None:
            message_chain.append(halMessage.HalMessage(source = self,
                                                       m_type = "new parameters file",
                                                       data = {"parameters filename" : parameters_file_name,
                                                               "is_default" : True}))

        # start.
        #
        # It is safe to stop blocking Qt's last window closed behavior after
        # this message as HAL's main window will be open.
        #
        # If we run HAL from another module, in testing for example, app might
        # be none.
        #
        if app is not None:
            message_chain.append(halMessage.HalMessage(source = self,
                                                       m_type = "start",
                                                       data = {"show_gui" : show_gui},
                                                       sync = True,
                                                       finalizer = lambda : app.setQuitOnLastWindowClosed(True)))

        else:
           message_chain.append(halMessage.HalMessage(source = self,
                                                      m_type = "start",
                                                      data = {"show_gui" : show_gui},
                                                      sync = True))
           message_chain.append(halMessage.SyncMessage(source = self))

        self.handleMessage(halMessage.chainMessages(self.handleMessage,
                                                    message_chain))
  1. **kwds为不必要的参数,设计函数参数时,参数由关键字参数和关键字参数字典组成,在调用链中,每一个函数获取其所需的关键字参数,保留不需要的参数到 **kwargs中,传递到调用链的下一个函数,最终 **kwargs为空时,调用调用链中的最后一个函数。
  2. 双端队列:queued_messages = deque()。
    queued_messages 消息通过队列来处理。
threadpool = QtCore.QThreadPool.globalInstance()

QThreadPool: 线程池
线程池的作用是管理、复用、回收一组线程,控制线程的数量,避免频繁的创建和销毁线程而浪费资源。
频繁创建、耗时短的任务使用线程池来执行更合适;

Qt中的线程池类为QThreadPool,全局的线程池可以用QThreadPool::globalInstance()得到,它默认最多创建8个线程,参考链接[1]展示了多个线程被创建和销毁的过程。

使用线程池

假设我们要创建一个任务task,继承QRunnable,task的对象作为threadpool.start(ct_task),线程池会自动的在线程池中调用task的run函数,异步执行,提交给线程池的 QRunnable 对象在它的 run() 函数执行完后会被自动 delete 掉,如果不想线程池删除它,在调用线程池的 start() 前调用 setAutoDelete(false) 即可。

pyqtSignal()

定义新的信号,新的信号作为类的属性;
新的信号应该定义在QtCore.QObject的子类中,

自建信号的触发

最开始理解pyqtSignal()时非常不好理解,等明白了发现是因为我之前没有信号发射的概念。
比如:
点击一个按钮与槽函数链接,通过clicked.connect我们能链接对应的槽函数并执行;
但是我们自己新建的信号怎么知道是什么时候触发自建的槽函数呢?当我们连接了自建信号与自建槽函数,接下来就需要emit()函数去执行发射信号这个过程,但什么时候去发射信号呢?这就要看具体情况了,甚至我们有时候需要再去点击某个按钮发射信号,那么又会多一步将按钮和对应的槽函数链接,而这个槽函数就包含了信号的触发函数(emit).
下面有个例子,来源于他人的博客:

#创建新的信号

printSignal = pyqtSignal(list)  

#槽函数·

def printPaper(self,list):
        self.resultLabel.setText("Print: "+"份数:"+ str(list[0]) +"  纸张:"+str(list[1]))#绑定信号与槽函数

#创建的信号与槽函数链接

self.printSignal.connect(self.printPaper) 

#通过点击按钮发射自建的信号
self.printButton.clicked.connect(self.emitPrintSignal)
#触发信号的函数

    def emitPrintSignal(self):
        pList = []
        pList.append(self.numberSpinBox.value ())
        pList.append(self.styleCombo.currentText())
        self.printSignal.emit(pList)  # 发射信号

从而完成了信号传输的一个闭环。

runWorkerTask

有些消息需要立即处理的,就得用runWorkerTask来处理,runWorkerTask开辟了一个新的单线程,并保留其他信息在队列中。

主程序运行

app.exec_()
一直对app为什么能在桌面上运行感到疑惑,之前是以为是show(),但storm_control的主文件中没有show()过,且程序运行完最后一句后才出现的程序界面,所以使程序运行的实际上是最后一句的app.exec_() ,其作用是保证进入程序的主循环直到exit()被调用

那么所谓的主循环 是什么呢?
主循环应该是指之前的程序中设定好的界面上,程序一直在等信号触发,除非遇到关闭信号。

参考链接

  1. https://qtdebug.com/qtbook-thread-pool/
  2. https://www.riverbankcomputing.com/static/Docs/PyQt5/signals_slots.html?highlight=pyqtsignal#PyQt5.QtCore.pyqtSignal
  3. https://blog.csdn.net/zhulove86/article/details/52563131

你可能感兴趣的:(硬件)