1. 程式人生 > >多執行緒中的訊號/槽

多執行緒中的訊號/槽

日期:
2008-01-05 於武漢

開始

在許多應用中都會遇到非常耗時的運算,在進行該型別運算時常常會影響程式正常的訊息處理。 為了處理上述問題,我們可以將耗時的運算從GUI執行緒中移出來,單獨放到一個work執行緒中。 這樣的話,GUI則可以保持時刻響應。

下面的例子中,我們將演示如何運用多執行緒。在一個work執行緒中將完成繪製五角星的操作, 在繪製完成後將發射訊號通知GUI執行緒進行顯示。下面是程式的執行效果:

使用者介面

首先匯入相關模組。其中math和random模組在繪製五角星的時候需要:

import math, random, sys
from PyQt4.QtCore import
* from PyQt4.QtGui import * class Window(QWidget): def __init__(self, parent = None): QWidget.__init__(self, parent) self.thread = Worker()

主視窗從QWidget繼承。同時建立一個Worker執行緒用於完成相關操作。

主視窗包含一個label,一個spin box(用於設定要繪製的五角星數目),一個按鈕,執行緒輸出的 圖象在另一個QLabel(viewer)中顯示。

        label = QLabel(self
.tr("Number of stars:")) self.spinBox = QSpinBox() self.spinBox.setMaximum(10000) self.spinBox.setValue(1000) self.startButton = QPushButton(self.tr("&Start")) self.viewer = QLabel() self.viewer.setFixedSize(300, 300)

執行緒的finished()和terminated()訊號被連線到self.updateUi,用於更新介面。output(QRect, QImage) 訊號連線到addImage(),用於繪製單個的五角星。

        self.connect(self.thread, SIGNAL("finished()"), self.updateUi)
        self.connect(self.thread, SIGNAL("terminated()"), self.updateUi)
        self.connect(self.thread, SIGNAL("output(QRect, QImage)"), self.addImage)
        self.connect(self.startButton, SIGNAL("clicked()"), self.makePicture)

按鈕的clicked()訊號連線到makePicture(),用於啟動work執行緒。每個部件元素通過grid佈局管理器 來管理。然後設定視窗標題。

        layout = QGridLayout()
        layout.addWidget(label, 0, 0)
        layout.addWidget(self.spinBox, 0, 1)
        layout.addWidget(self.startButton, 0, 2)
        layout.addWidget(self.viewer, 1, 0, 1, 3)
        self.setLayout(layout)
        
        self.setWindowTitle(self.tr("Simple Threading Example"))

makePicture()主要完成個操作:1. 禁止修改介面;2. 重新生成一個新的pixmap;3. 開始工作 執行緒的繪製操作。

    def makePicture(self):
    
        self.spinBox.setReadOnly(True)
        self.startButton.setEnabled(False)

        pixmap = QPixmap(self.viewer.size())
        pixmap.fill(Qt.black)
        self.viewer.setPixmap(pixmap)

        self.thread.render(self.viewer.size(), self.spinBox.value())

我們用viewer的大小和五角星的數目作為引數傳遞給work執行緒的render函式進行繪製操作。 其中五角星的數目從滑塊獲取(spinBox.value())。

當work完成一個五角星的繪製時,會發射一個訊號,呼叫addImage()槽。addImage()槽根據五角星的 所在位置和對應的pixmap在view中顯示。然後更新視窗。

    def addImage(self, rect, image):
    
        pixmap = self.viewer.pixmap()
        painter = QPainter()

        painter.begin(pixmap)
        painter.drawImage(rect, image)
        painter.end()

        self.viewer.update(rect)

我們通過QPainter完成繪製操作。

updateUi()槽在work執行緒完成全部操作的時候被觸發。同時恢復視窗按鈕和滑塊的狀態。

    def updateUi(self):
    
        self.spinBox.setReadOnly(False)
        self.startButton.setEnabled(True)

前面我們已經知道怎麼在一個視窗部件中使用work執行緒,下面是work的具體實現。

Worker執行緒

為了能線上程中更好的使用Qt的訊號槽特性,我們使用PyQt中的執行緒來代替Python本身的執行緒機制。

class Worker(QThread):

    def __init__(self, parent = None):
    
        QThread.__init__(self, parent)
        self.exiting = False
        self.size = QSize(0, 0)
        self.stars = 0

在work執行緒中儲存一些基本的繪製資訊,並對它們進行初始化。其中exiting用於記錄執行緒的 工作狀態。

每個五角星都通過QPainterPath繪製:

        self.path = QPainterPath()
        angle = 2*math.pi/5
        self.outerRadius = 20
        self.innerRadius = 8
        self.path.moveTo(self.outerRadius, 0)
        for step in range(1, 6):
            self.path.lineTo(
                self.innerRadius * math.cos((step - 0.5) * angle),
                self.innerRadius * math.sin((step - 0.5) * angle)
                )
            self.path.lineTo(
                self.outerRadius * math.cos(step * angle),
                self.outerRadius * math.sin(step * angle)
                )
        self.path.closeSubpath()

當work執行緒物件在被銷燬的時候,需要停止執行緒。在__del__函式中呼叫執行緒的wait()等待 執行緒的退出。

    def __del__(self):
    
        self.exiting = True
        self.wait()

在渲染五角星之前,我們先記錄相關的繪製資訊,然後開啟執行緒。

    def render(self, size, stars):
    
        self.size = size
        self.stars = stars
        self.start()

start()方式用來啟動執行緒,並且執行執行緒類中的run()方法。在這裡我們重新實現了run()方法。 我們通過render()函式來代替直接呼叫run(),這樣我們就可以通過render給執行緒傳遞相關資訊。 run()方法定義如下:

    def run(self):
        
        # Note: This is never called directly. It is called by Qt once the
        # thread environment has been set up.
        
        random.seed()
        n = self.stars
        width = self.size.width()
        height = self.size.height()

前面已經儲存的屬性資訊對應要繪製五角星的數目和對應的繪製區域。

在繪製每個五角星的時候,我們檢測self.exiting的狀態,這樣可以確保在任何時刻都可以退出 執行緒。

        while not self.exiting and n > 0:
        
            image = QImage(self.outerRadius * 2, self.outerRadius * 2,
                           QImage.Format_ARGB32)
            image.fill(qRgba(0, 0, 0, 0))
            
            x = random.randrange(0, width)
            y = random.randrange(0, height)
            angle = random.randrange(0, 360)
            red = random.randrange(0, 256)
            green = random.randrange(0, 256)
            blue = random.randrange(0, 256)
            alpha = random.randrange(0, 256)
            
            painter = QPainter()
            painter.begin(image)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setPen(Qt.NoPen)
            painter.setBrush(QColor(red, green, blue, alpha))
            painter.translate(self.outerRadius, self.outerRadius)
            painter.rotate(angle)
            painter.drawPath(self.path)
            painter.end()

具體的繪製程式碼和多執行緒關係並不大,我們只是在區域內隨機地點繪製不同的五角星。

當每個五角星被繪製完成時刻,我們通過發射"output(QRect, QImage)"訊號來通知GUI執行緒 來執行相關的操作。

            self.emit(SIGNAL("output(QRect, QImage)"),
                      QRect(x - self.outerRadius, y - self.outerRadius,
                            self.outerRadius * 2, self.outerRadius * 2), image)
            n -= 1

用這種方式可以在不同執行緒之間傳遞QRect和QImage物件。

啟動程式碼

if __name__ == "__main__":

    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

全部程式碼

相關推薦

PyQt訊號執行訊號的使用(六)

簡單多執行緒訊號與槽的使用 最簡單的多執行緒使用方法是利用QThread函式,展示QThread函式和訊號簡單結合的方法 import sys from PyQt5.QtCore import

QT執行,物件訊號連線不上的解決辦法

1、在接收者建立執行緒中,把接收者移動到主執行緒中: pReceiverObj->moveToThread(QApplication::instance()->thread()); 2、這樣傳送訊號的時候,就會在主執行緒事件佇列處理中來處理了。 把connect的最

執行訊號/

日期:2008-01-05 於武漢開始 在許多應用中都會遇到非常耗時的運算,在進行該型別運算時常常會影響程式正常的訊息處理。 為了處理上述問題,我們可以將耗時的運算從GUI執行緒中移出來,單獨放到一個work執行緒中。 這樣的話,GUI則可以保持時刻響應。 下面的例子中,我們

執行訊號機制--sigwait 函式

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux執行使用訊號 (轉)

       但是在多執行緒中處理訊號的原則卻完全不同,它的基本原則是:將對訊號的非同步處理,轉換成同步處理,也就是說用一個執行緒專門的來“同步等待”訊號的到來,而其它的執行緒可以完全不被該訊號中斷/打斷(interrupt)。這樣就在相當程度上簡化了在多執行緒環境中對訊號的處理。而且可以保證其它的執行緒不受

執行訊號機制--sigwait()函式

這個例子演示了:通過在主執行緒中阻塞一些訊號,其它的執行緒會繼承訊號掩碼,然後專門用一個執行緒使用sigwait函式來同步的處理訊號,使其它的執行緒不受到訊號的影響。#include <pthread.h> #include <stdio.h> #include <sys/sig

Linux執行使用訊號-1

pthread_sigmask - examine and change mask of blocked signals#include <signal.h>int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);Co

Java執行Synchronized簡介和Static Synchronized的區別

在進行Java開發時,多執行緒的開發是經常會使用的。首先會問一個小問題啊,在Java中有幾種方法可以建立一個執行緒? 我給的答案是3種。(如果還有其他的請留言告訴我哈。) 1、建立直接繼承自Thread類建立執行緒子類。   步驟如下:a 定義一個子類,同時

執行訊號量的簡單使用 GCD

基本概念 關於iOS開發中,多執行緒基本的概念和基本使用,我在這裡就不在重複說了。但是為了照顧到有的同學可能還不是對基本的概念不熟悉,可以參考一下這篇文章併發其實很簡單 說說訊號量,併發數 如果你有計算機基礎,那麼下面這段話應該很簡單就能理解 訊號量就是一個資源計數器,對訊號

執行的佇列不一定需要執行安全

兩個執行緒,主執行緒中update update(){   while(queue.count >0){     //process....     queue.pop()   } }   子執行緒中: queue.enqueue(data)   這樣做是沒有問

linux 執行訊號量 sem_init

1. 什麼是訊號量 linux sem 訊號量是一種特殊的變數,訪問具有原子性, 用於解決程序或執行緒間共享資源引發的同步問題。 使用者態程序對 sem 訊號量可以有以下兩種操作: 等待訊號量 當訊號量值為 0 時,程式等待;當訊號量值大於 0 時,訊號量減 1,程式

junit測試和main方法執行遇到的問題

利用Junit測試多執行緒時經常遇到任務執行不完就會停止,下面是我的任務執行緒類: package timerTest; import java.io.BufferedReader; import java.io.BufferedWriter; import java.i

PYTHON——執行訊號量(Semaphore)

  訊號量也是一把鎖,用來控制執行緒併發數的。   BoundedSemaphore或Semaphore管理一個內建的計數 器,每當呼叫acquire()時-1,呼叫release()時+1。       計數器不能小於0,當計數器為 0時,acquire()將阻塞執行緒至同

如何在執行執行注入bean

轉:https://segmentfault.com/q/1010000007172414?_ea=1264473 如何在多執行緒中注入bean?!​​​​​​​ 問題對人有幫助,內容完整,我也想知道答案0問題沒有實際價值,缺少關鍵內容,沒有改進餘地 前幾天,在sf這裡也提過這個問題,但

謹慎使用執行的fork

前言 在單核時代,大家所編寫的程式都是單程序/單執行緒程式。隨著計算機硬體技術的發展,進入了多核時代後,為了降低響應時間,重複充分利用多核cpu的資源,使用多程序程式設計的手段逐漸被人們接受和掌握。然而因為建立一個程序代價比較大,多執行緒程式設計的手段也就逐漸被人們認可和喜愛了。 記得在我剛

執行的鎖

樂觀鎖和悲觀鎖 樂觀鎖和悲觀鎖是在資料庫中引入的名詞,但是在併發包鎖裡面也引入了類似的思想,所以這裡還是有必要講解一下。 悲觀鎖指對資料被外界修改持保守的態度,認為資料很容易就會被其他執行緒修改,所以在資料被處理前就先對資料加鎖,並在整個資料處理過程中,使資料處於鎖定狀態,悲觀鎖的實現往往依靠資料庫

java執行的異常處理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

java執行顯式鎖的輪詢檢測策略

顯式鎖簡介 java5.0之前,在協調對共享物件的訪問時可以使用的機制只有synchronized和volatile,java5.0增加了一種新的機制:ReentrantLock。 鎖像synchronized同步塊一樣,是一種執行緒同步機制,與synchronized不同的是ReentrantLock提

C++執行的future(期望)

Providers std::promise 和std::future配合使用,給std::future傳遞期望值,下面是最簡單的一個用法: #include <iostream> #include <functional> #include <

Java執行注入Spring的Bean-使用靜態方法直接取的容器的spring物件

目前認為比較好的解決方案。 1,工具類 public class SpringApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context