<dl></dl>
<dt>作者:</dt>
<dt>日期:</dt>
<dd>2008-01-05 于武漢</dd>
<dt>注解:</dt>
在許多應用中都會遇到非常耗時的運算,在進行該類型運算時常常會影響程式正常的消息處理。 為了處理上述問題,我們可以将耗時的運算從GUI線程中移出來,單獨放到一個work線程中。 這樣的話,GUI則可以保持時刻響應。
下面的例子中,我們将示範如何運用多線程。在一個work線程中将完成繪制五角星的操作, 在繪制完成後将發射信号通知GUI線程進行顯示。下面是程式的運作效果:
首先導入相關子產品。其中math和random子產品在繪制五角星的時候需要:
主視窗從QWidget繼承。同時建立一個Worker線程用于完成相關操作。
主視窗包含一個label,一個spin box(用于設定要繪制的五角星數目),一個按鈕,線程輸出的 圖象在另一個QLabel(viewer)中顯示。
線程的finished()和terminated()信号被連接配接到self.updateUi,用于更新界面。output(QRect, QImage) 信号連接配接到addImage(),用于繪制單個的五角星。
按鈕的clicked()信号連接配接到makePicture(),用于啟動work線程。每個部件元素通過grid布局管理器 來管理。然後設定視窗标題。
makePicture()主要完成個操作:1. 禁止修改界面;2. 重新生成一個新的pixmap;3. 開始工作 線程的繪制操作。
我們用viewer的大小和五角星的數目作為參數傳遞給work線程的render函數進行繪制操作。 其中五角星的數目從滑塊擷取(spinBox.value())。
當work完成一個五角星的繪制時,會發射一個信号,調用addImage()槽。addImage()槽根據五角星的 所在位置和對應的pixmap在view中顯示。然後更新視窗。
我們通過QPainter完成繪制操作。
updateUi()槽在work線程完成全部操作的時候被觸發。同時恢複視窗按鈕和滑塊的狀态。
前面我們已經知道怎麼在一個視窗部件中使用work線程,下面是work的具體實作。
為了能線上程中更好的使用Qt的信号槽特性,我們使用PyQt中的線程來代替Python本身的線程機制。
在work線程中儲存一些基本的繪制資訊,并對它們進行初始化。其中exiting用于記錄線程的 工作狀态。
每個五角星都通過QPainterPath繪制:
當work線程對象在被銷毀的時候,需要停止線程。在__del__函數中調用線程的wait()等待 線程的退出。
在渲染五角星之前,我們先記錄相關的繪制資訊,然後開啟線程。
start()方式用來啟動線程,并且運作線程類中的run()方法。在這裡我們重新實作了run()方法。 我們通過render()函數來代替直接調用run(),這樣我們就可以通過render給線程傳遞相關資訊。 run()方法定義如下:
前面已經儲存的屬性資訊對應要繪制五角星的數目和對應的繪制區域。
在繪制每個五角星的時候,我們檢測self.exiting的狀态,這樣可以確定在任何時刻都可以退出 線程。
具體的繪制代碼和多線程關系并不大,我們隻是在區域内随機地點繪制不同的五角星。
當每個五角星被繪制完成時刻,我們通過發射"output(QRect, QImage)"信号來通知GUI線程 來執行相關的操作。
用這種方式可以在不同線程之間傳遞QRect和QImage對象。
<a href="http://pyqt-doc-cn.googlecode.com/svn/trunk/Threading_Signals_and_Slots/doc/r54/Threading_Signals_and_Slots.pyw">全部代碼</a>