這是一篇翻譯過來的文章,是Qt和QML互動的一篇官方文章,原文地址在: http://developer.qt.nokia.com/wiki/Updating_QML_content_from_Python_threads
這篇文章,是用PySide來製作的,因此練習之前,需要先安裝PySide(Qt的另一種Python實現,由Nokia官方實現)。PySide的可以在下面下載:
http://developer.qt.nokia.com/wiki/Category:LanguageBindings::PySide::Downloads
如果你和我一樣,使用的是Windows上的Python 2.6,則可以在下面路徑下載:
http://www.pyside.org/files/PySide-1.0.7qt474.win32-py2.6.exe
=============================================================
本文的PySide教程展現了如何使用內建的Python線程(非QThread,如threading.Thread)來負責背景的繪製(如下載檔案)。在下載這個特殊的例子中,可能使用QNetworkAccessManager更加友好,但這是個例子,我們假定由於某種原因,你不能夠使用QNetworkAccessManager這個類(如因為使用了Twisted或者因為已經有了特殊的下載代碼,並且希望能夠重用)。
WorkingOnIt.py
匯入需要的模組
我們將使用標準的Python線程模組(threading)和使用下載庫(urllib)。對於PySide程式,我們需要標準的模組QtCore,QtGui和QtDeclarative:
import os
import sys
import threading
import urllib
from PySide import QtCore, QtGui, QtDeclartive
下載對象Downloader
建立QObject的子類(這樣我們可以在自己的下載對象中使用訊號,槽和屬性)和實現下載檔案所需的所有屬性以及在UI上顯示目前狀態。
class Downloader(QtCore.QObject):
def __init__(self, url, filename=None):
super(Downloader, self).__init__()
self._url = url
if filename is None:
filename = os.path.basename(self._url)
self._filename = filename
self._progress = 0
self._running = False
self._size = -1
def _download(self):
def reporthook(pos, block, total):
if self.size != total:
self._size = total
self.on_size.emit()
self.progress = float(pos * block ) / float(total)
urllib.urlretrieve(self._url, self._filename, reporthook)
self.running = False
@QtCore.Slot()
def start_download(self):
if not self.running:
self.running = True
thread = threading.Thread(target = self._download)
thread.start()
def _get_progress(self):
return self._progress
def _set_progress(self, progress):
self._progress = progress
self.on_progress.emit()
def _get_running(self):
return self._running
def _set_running(self, running):
self._running = running
self.on_running.emit()
def _get_filename(self):
return self._filename
def _get_size(self):
return self._size
on_progress = QtCore.Signal()
on_running = QtCore.Signal()
on_filename = QtCore.Signal()
on_size = QtCore.Signal()
progress = QtCore.Property(float, _get_progress, _set_progress, notify=on_progress)
running = QtCore.Property(bool, _get_running, _set_running, notify=on_running)
filename = QtCore.Property(bool, _get_filename, notify=on_filename)
size = QtCore.Property(bool, _get_size, notify=on_size)
建立一個新的Downloader執行個體:
作為一個例子,我們建立一個新的Downloader對象,用來從MeeGo庫中下載N900的核心映像。(實際上測試的時候,發現該映像已經不能夠被下載了,坑爹呀!,原先的代碼是:
downloader = Downloader('http://repo.meego.com/MeeGo/builds/trunk/1.1.80.8.20101130.1/handset/images/meego-handset-armv7l-n900/meego-handset-armv7l-n900-1.1.80.8.20101130.1-vmlinuz-2.6.35.3-13.6-n900')
我替換為:)
downloader = Downloader('http://www.pyside.org/files/PySide-1.0.7qt474.win32-py2.6.exe')
QApplication,QDeclarativeView和內容屬性
通常,我們建立一個QApplication和QDeclarativeView的執行個體。通過設定Downloader為view中的rootContext的內容屬性downloader,將Downloader匯出到QML上下文中。然後簡單的通過setSource匯入QML檔案,show出視圖並執行應用:
app = QtGui.QApplication(sys.argv)
view = QtDeclarative.QDeclarativeView()
view.rootContext().setContextProperty("downloader", downloader)
view.setSource(__file__.replace('.py', '.qml'))
view.show()
sys.exit( app.exec_() )
WorkingOnIt.qml
該檔案為downloader執行個體中的QML UI介面,最令人感興趣的是:
- 當按鈕按下的時候,downloader.start_download()(一個PySide的槽)被調用,該方法啟動線程
- 當UI元素使用downloader的屬性來決定元素可視與內容-當屬性發出修改通知時自動更新。
import Qt 4.7
Rectangle{
width:200; height:160;
function formatProgress(size, progress){
return "" + parseInt(progress*size/1024) + "KiB(" + parseInt(progress * 100.) + "%)";
}
Text {
x : progressBar.x; y:20;
width: progressBar.width;
font.pixelSize:8;
text:downloader.filename;
elide:Text.ElideRight;
}
Rectangle{
id:progressBar;
color:"#aaa"
x:20; y:60;
width:parent.width-40;
height:20;
Rectangle{
color:downloader.progress<1?"#ee8":"#8e8"
clip:true
anchors{
top:parent.top
bottom:parent.bottom
left:parent.left
}
width:parent.width * downloader.progress;
Text{
anchors{
fill:parent;
rightMargin:5;
}
color:"black";
text:formatProgress(downloader.size, downloader.progress)
verticalAlignment:Text.AlignVCenter;
horizontalAlignment:Text.AlignRight;
}
}
}
Rectangle{
anchors.left : progressBar.left;
anchors.right: progressBar.right;
color : "#aad";
y : progressBar.y + progressBar.height + 20;
height : 40;
Text{
anchors.fill:parent;
color:"#003";
text:downloader.running?"Please wait..." : "Start download"
verticalAlignment : Text.AlignVCenter;
horizontalAlignment : Text.AlignHCenter;
}
MouseArea{
anchors.fill : parent;
onClicked : {
downloader.start_download();
}
}
}
}
(譯註:沒有合適的文法高亮,使用CSS類似的文法高亮機制。)
例子執行的外觀
在同一個檔案夾中儲存檔案WorkingOnIt.py和WorkingOnIt.qml,使用python WorkingOnIt.py執行應用。(官方的圖片如下)
=============================================================
我在windows上執行的時候,發現顯示的時候會閃一下。
這樣的互動方式其實是Qt與JavaScript互動的一個重要特性。QML使用的是JavaScript文法進行控制操作,而Qt本身就內建支援了JavaScript文法,在Qt對象匯出給JavaScript時,屬性,槽是可以直接在JavaScript中調用的,這個在QScriptValue文檔中就有明確的說明。