代码改变世界

用Python线程中更新QML内容

2011-11-18 22:24  ubunoon  阅读(1801)  评论(0编辑  收藏  举报

这是一篇翻译过来的文章,是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,QtGuiQtDeclarative:

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执行应用。(官方的图片如下)

 

thread-in-python-update-in-qml

 

=============================================================

我在windows上执行的时候,发现显示的时候会闪一下。

 

这样的交互方式其实是Qt与JavaScript交互的一个重要特性。QML使用的是JavaScript语法进行控制操作,而Qt本身就内建支持了JavaScript语法,在Qt对象导出给JavaScript时,属性,槽是可以直接在JavaScript中调用的,这个在QScriptValue文档中就有明确的说明。