用QtQuick创建应用程序 - 用声明式QML构建现代应用程序

·  阅读 101
用QtQuick创建应用程序 - 用声明式QML构建现代应用程序

在以前的教程中,我们使用Qt Widgets API来构建我们的应用程序。自Qt首次开发以来,这一直是构建应用程序的标准方法。然而,Qt提供了另一个用于构建用户界面的API。Qt Quick。这是一个现代的以移动为重点的应用开发API,利用它你可以创建动态的、高度可定制的用户界面。

QtQuick

Qt Quick使用一种声明性脚本语言--Qt建模语言(QML)--来定义用户界面。有了它,你可以建立完全自定义的用户界面,具有动态图形元素和流畅的过渡和效果。用QML构建的用户界面与移动应用的共同点多于传统的桌面应用,这反映了它与诺基亚的渊源,但Qt Quick可以在Qt支持的所有平台上使用。

QML语法也支持嵌入式Javascript,它可以用来处理应用逻辑--在简单的应用中,整个应用可以在QML中实现!但使用PySide,你也可以用Python编写你的应用代码,并将其与QML挂钩。这样做的好处是保持你的UI设计(QML)和业务逻辑(Python)的实现适当隔离,并让你访问所有的Python库,以支持你的应用程序的后端。

在开始本教程之前,你需要安装PySide,参见安装指南

对于构建QML应用程序,你可以使用PySide2或PySide6。如果使用Qt 6,你将需要v6.1或更高版本。

开始使用

在本教程中,我们将使用PySide与Qt Quick/QML API。 如果你以前使用过Qt Widgets,许多Qt Quick的概念会显得很熟悉。虽然QML不使用QtWidget 类,但Qt的所有其他部分(QtCore,QtGui, 等等)仍然可用。

在我们开始编写应用程序之前,我们可以在下面的正确结构中用我们需要的文件设置我们的项目文件夹。你也可以下载一个包含这些文件的压缩文件

  • 为应用程序创建一个项目文件夹,在我们的例子中,我们将把它称为。clock
  • 在你的clock 文件夹内创建一个空文件,名为main.py
  • main.py 旁边创建一个名为main.qml 的文件,以保存我们在QML中的UI定义。
  • main.pymain.qml 的旁边创建一个空文件夹,名为images

创建一个 "Hello World "应用程序

在你的编辑器中打开main.py ,并添加以下骨架代码。这是使用QML应用引擎加载QML文件并显示它所需的最低限度的代码。

main.py

  • PySide2
  • PySide6
import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine


app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

sys.exit(app.exec_())
复制代码
import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine


app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

sys.exit(app.exec_())
复制代码

上述代码调用QGuiApplicationQQmlApplicationEngine 这将使用QML而不是QtWidgets作为Qt应用程序的UI层。然后它将UI层的退出函数与应用程序的主退出函数连接起来。因此,当UI层被用户关闭时,两者都可以关闭。接下来,它加载QML文件作为用户界面的QML代码。app.exec() ,启动Qt事件循环并启动应用程序,就像在Qt Widgets中一样。

在这里,对app.exec() 的调用被包裹在sys.exit() 内,以便在出现错误时向调用进程返回退出代码,但这并不是严格必要的。

接下来,在main.qml 中添加以下代码。

main.qml

qml

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 600
    height: 500
    title: "HelloApp"

    Text {
        anchors.centerIn: parent
        text: "Hello World"
        font.pixelSize: 24
    }

}
复制代码

上面的代码创建了一个Window,宽度和高度都是指定的,标题是HelloApp,还有一个Text对象,它在父对象(在这里是指窗口)中居中。 显示的文本是 "Hello World",像素大小为24px。

visible: true 是非常重要的,没有它,用户界面将被创建,但将是不可见的!

一旦你把上述代码输入两个文件并保存,你就可以运行它并看到结果。 你可以像其他Python脚本一样运行代码--导航到包含main.py 脚本的文件夹,用python (或python3 ,取决于你的系统)运行它。

外壳

$ cd clock
$ python main.py
复制代码

当应用程序启动时,你应该看到一个像下面这样的窗口:

Hello World shown in an application

成功了!我们有了一个QML应用程序,尽管它开始时非常基本。 接下来我们将修改用户界面,使它更有趣一些,并建立一个简单的时钟。

更新UI设计

首先让我们添加一张图片作为背景。

这张图片放在我们之前创建的名为images 的文件夹中。 这将是我们的应用程序窗口的背景。

A simple background image with a gradient effect 一个具有渐变效果的简单背景图片

如果你喜欢,你可以用你的任何其他图片代替。我们将在上面放置白色的文本,所以深色的简单图片效果会更好。

main.qml

qml

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 400
    height: 600
    title: "Clock"

    Rectangle {
        anchors.fill: parent

        Image {
            anchors.fill: parent
            source: "./images/background.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-600 600w" loading="lazy" width="564" height="1003"
            fillMode: Image.PreserveAspectCrop
        }

        Rectangle {
            anchors.fill: parent
            color: "transparent"

            Text {
                text: "16:38:33"
                font.pixelSize: 24
                color: "white"
            }

        }

    }

}
复制代码

在这个QML文件中,我们使用ApplicationWindow 对象类型定义了我们的主应用程序窗口。在这个文件中,我们定义了一个Rectangle 和一个Image ,它容纳了我们的背景图像,填充了父级。fillMode 定义了图像的大小。在这个例子中,我们使用anchors.fill: parent 来设置图像以填充父窗口,同时保留长宽比和裁剪。这确保了图像填满了窗口区域,而没有变形。你也可以通过设置sourceSize 属性来控制内存中图像的大小,例如。

qml

        Image {
            sourceSize.width: parent.width
            sourceSize.height: parent.height
            source: "./images/background.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-600 600w" loading="lazy" width="564" height="1003"
            fillMode: Image.PreserveAspectCrop
        }
复制代码

这种方法允许你有更多的控制权--例如,你可以通过将尺寸一分为二,将图像缩放到父窗口的一半大小,并以此来平铺多个图像。

qml

        Image {
            sourceSize.width: parent.width/2
            sourceSize.height: parent.height/2
            source: "./images/background.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-600 600w" loading="lazy" width="564" height="1003"
            fillMode: Image.PreserveAspectCrop
        }
复制代码

Image ,我们还定义了一个透明的Rectangle ,它也填充了整个窗口。由于矩形是在Image 旁边定义的,你可能会认为它在用户界面中会出现在相邻的地方,但是由于在窗口中没有定义布局,所以元素是堆叠的--Rectangle 出现在Image 的上面。

默认情况下,Rectangle 对象有一个白色背景。

最后,在矩形内,我们定义了一个带有 "16:38:33 "文字的Text 对象来模拟一个标准的时间显示。

如果你现在运行应用程序,该文本将出现在我们的应用程序窗口的左上角。

$ python main.py
复制代码

By default text appears in the top left 默认情况下,文本出现在左上角

让我们把它移到别的地方--移到左下角,用一些边距来使它看起来更漂亮。在你的QML代码中,更新Text 对象以包括Text 的位置锚,并改变字体的大小和颜色。

main.qml

qml

            Text {
                anchors {
                    bottom: parent.bottom
                    bottomMargin: 12
                    left: parent.left
                    leftMargin: 12
                }
                text: "16:38:33"
                font.pixelSize: 48
                color: "white"
            }

复制代码

像以前一样再次运行该应用程序。

$ python main.py
复制代码

你会看到文本现在已经移到了左下方。

Application window with text in the bottom left 文字在左下角的应用程序窗口

到目前为止,我们的时间显示只是一个固定的文本字符串--它不更新,除非你在正确的时间运行它,否则它将是错误的。 这并不是最有用的时钟!接下来我们将添加一些Python代码来获取当前的系统时间并自动更新我们的时钟显示。

从Python中获取时间

Python标准库提供了处理时间和日期的函数,包括一些获取当前时间的选项。例如,Python函数time.gmtime() 提供了一个包含当前GMT时间的结构,而time.localtime()将提供你当前本地时区的时间。

一旦你有了一个时间struct ,你就可以把它传给time.strftime() 函数,得到一个正确格式化的字符串。strftime 函数接受两个参数--第一个是时间格式字符串,第二个是要使用的时间结构。时间格式字符串使用诸如%H 等标记,将时间日期的特定部分置于特定格式中。

例如,如果你在Python shell中输入以下内容,你会得到当前的GMT(UTC)时间输出。

蟒蛇

from time import strftime, gmtime
strftime("%H:%M:%S", gmtime())
复制代码

%H,%M%S 标记告诉strftime 将小时(24小时,零填充)、分钟(零填充)和秒(零填充)插入字符串中。

你可以在Python文档中阅读更多关于strftime 的格式代码。

对于本地时间,你可以使用localtime 方法,而不是gmtime

python

from time import strftime, localtime
strftime("%H:%M:%S", localtime())
复制代码

这将根据你的计算机的本地时间设置进行调整,并应输出与你的计算机时钟上显示的时间相同的时间。

如果你习惯于使用datetime 对象,每一个datetime 都有一个.strftime() 方法,它使用相同的格式字符串并返回相同的输出。例如,下面将给出与上述localtime 示例相同的输出。

蟒蛇

from time import strftime
from datetime import datetime
datetime.now().strftime("%H:%M:%S")
复制代码

更新我们的应用程序的时间显示

为了把我们的格式化时间字符串从Python传到QML,我们可以使用QML属性。首先,让我们在我们的QMLApplicationWindow 上定义一个属性,叫做currTime 。在main.qml 中更新你的QML代码,如下所示。

main.qml

qml

...
ApplicationWindow {
    ...
    title: "Clock"
    property string currTime: "00:00:00"

    ...

复制代码

... 标记表示现有代码应保持原样。

接下来,修改文本对象,使用currTime 属性作为它的text 值。当currTime 属性被修改时,文本标签将自动更新(连同任何其他使用它的地方)。

main.qml

qml

...
            Text {
                ...
                text: currTime  // used to be; text: "16:38:33"
                font.pixelSize: 48
                color: "white"
            }

...

复制代码

最后,我们需要将存储在curr_time 变量中的当前时间从我们的Python代码中发送到QML。修改Python代码以添加时间格式化代码,使用localtime() ,然后将这个属性设置到QML对象上。下面的代码将把QML的属性currTime 设置为Python变量curr_time 的值。

main.py

  • PySide2
  • PySide6

蟒蛇

import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

# Pass the current time to QML.
curr_time = strftime("%H:%M:%S", localtime())
engine.rootObjects()[0].setProperty('currTime', curr_time)

sys.exit(app.exec_())
复制代码

蟒蛇

import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

# Pass the current time to QML.
curr_time = strftime("%H:%M:%S", localtime())
engine.rootObjects()[0].setProperty('currTime', curr_time)

sys.exit(app.exec_())
复制代码

代码engine.rootObjects()[0] ,从QML引擎中获取所有的根对象作为一个列表。我们的ApplicationWindow 对象是一个对象,因为它出现在层次结构的顶端。接下来,我们使用[0] 来选择该列表中的第一个项目--在我们的例子中,只有一个项目,即我们的ApplicationWindow ,这就是返回的内容。然后对该对象调用.setProperty 方法。

如果你现在运行这个应用程序,你应该看到窗口中显示的时间是正确的。这意味着当应用程序启动时,时间将是正确的 -- 试着关闭它并重新运行它以看到时间的更新。但是你会注意到,时间还没有更新 -- 我们接下来会这样做。

The correct time (at least it was when I took the screenshot) 正确的时间(至少在我截图的时候是这样的)

使用定时器更新时间

为了更新计时器,我们需要以固定的时间间隔(每秒)运行我们的时间获取和格式化代码。有两个选择来实现这一点

  1. 使用一个定时器,每隔1/10秒触发一次我们的时间方法
  2. 使用一个长期运行的线程,在每次更新之间有一个延迟(睡眠)来计算时间。

在Qt中,定时器是在GUI线程的主循环中处理的,这意味着每次定时器被触发时,GUI都会在函数执行时被阻塞。如果该函数长时间运行,这在用户界面中会变得很明显。在这种情况下,使用一个线程会更有意义。但是在这里,我们的时间获取和格式化代码是非常快的 -- 不会有明显的延迟。出于这个原因,在这个例子中我们将使用一个简单的定时器。

设置属性(setProperty

根据我们到目前为止的代码,自动更新时间的最简单的方法就是把我们的更新代码,包在一个函数里面,然后用一个定时器反复调用这个函数。下面的代码显示了这种方法的作用,使用一个QTimer ,间隔为100msecs(1/10秒)。

main.py

  • PySide2
  • PySide6

蟒蛇

import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QTimer

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

def update_time():
    # Pass the current time to QML.
    curr_time = strftime("%H:%M:%S", localtime())
    engine.rootObjects()[0].setProperty('currTime', curr_time)

timer = QTimer()
timer.setInterval(100)  # msecs 100 = 1/10th sec
timer.timeout.connect(update_time)
timer.start()

sys.exit(app.exec_())
复制代码

蟒蛇

import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QTimer

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

def update_time():
    # Pass the current time to QML.
    curr_time = strftime("%H:%M:%S", localtime())
    engine.rootObjects()[0].setProperty('currTime', curr_time)

timer = QTimer()
timer.setInterval(100)  # msecs 100 = 1/10th sec
timer.timeout.connect(update_time)
timer.start()

sys.exit(app.exec_())
复制代码

如果你运行这个,你会看到时间正确地更新。

你可能还注意到,当应用程序第一次运行时,时间显示为'00:00:00'(默认值)一秒钟。这是因为用户界面在第一个间隔定时器执行之前就被渲染了。你可以通过在调用app.exec() 之前添加对update_time() 的调用来避免这种情况,例如:

蟒蛇

update_time() # initial startup
sys.exit(app.exec_())
复制代码

现在,当应用程序启动时,它将立即显示正确的时间。

使用信号

虽然这种从Python代码中设置属性的方法在这个小例子中很好用,但当你的应用程序规模扩大时就不理想了。通过钩住你的Python代码来改变你的QML中的特定属性,你正在将你的Python代码与UI的结构联系起来。这使得在重组你的应用程序时很容易出现问题。就像在Qt Widgets API中,你可以使用Qt信号来避免这个问题:你的代码可以发射信号,而不需要知道它们将在哪里被接收,或者它们将如何被使用--你甚至可以把一个信号挂到多个接收器上。这就使你的逻辑和用户界面代码很好地解耦了。

如果你不熟悉Qt信号,可以看看我们的Signals, Slots & Events 教程

让我们重做我们的例子,在Python和QML中利用Qt信号来达到同样的效果。

首先,我们必须在main.py 文件中定义我们的信号。信号只能被定义在从QObject 子类化的对象上,所以我们需要实现一个小类。这也是一个合理的地方,可以把我们的时间处理代码放在这里,使事情保持良好的自洽性。我们还将定义我们的信号,用于向QML传递当前时间。

多个信号可以在一个单一的QObject 类下处理,为了简单起见,使用一个单一的类往往是有意义的。

main.py

  • PySide2
  • PySide6

蟒蛇

import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QTimer, QObject, Signal

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')

class Backend(QObject):

    updated = Signal(str, arguments=['time'])

    def __init__(self):
        super().__init__()

        # Define timer.
        self.timer = QTimer()
        self.timer.setInterval(100)  # msecs 100 = 1/10th sec
        self.timer.timeout.connect(self.update_time)
        self.timer.start()

    def update_time(self):
        # Pass the current time to QML.
        curr_time = strftime("%H:%M:%S", localtime())
        self.updated.emit(curr_time)


# Define our backend object, which we pass to QML.
backend = Backend()

engine.rootObjects()[0].setProperty('backend', backend)

# Initial call to trigger first update. Must be after the setProperty to connect signals.
backend.update_time()

sys.exit(app.exec_())
复制代码

蟒蛇

import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QTimer, QObject, Signal

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')


class Backend(QObject):

    updated = Signal(str, arguments=['time'])

    def __init__(self):
        super().__init__()

        # Define timer.
        self.timer = QTimer()
        self.timer.setInterval(100)  # msecs 100 = 1/10th sec
        self.timer.timeout.connect(self.update_time)
        self.timer.start()

    def update_time(self):
        # Pass the current time to QML.
        curr_time = strftime("%H:%M:%S", localtime())
        self.updated.emit(curr_time)


# Define our backend object, which we pass to QML.
backend = Backend()

engine.rootObjects()[0].setProperty('backend', backend)

# Initial call to trigger first update. Must be after the setProperty to connect signals.
backend.update_time()

sys.exit(app.exec_())
复制代码

虽然这看起来有很多变化,但大部分的代码是完全一样的,只是重新组织了一下,把所有的东西都放在容器类下。当我们使用backend = Backend() 创建一个Backend 类的实例时,__init__ 中的所有内容都将被运行。

信号定义(在下面重复)创建了一个信号,它接受一个参数--一个字符串。这将与信号一起被发送到接收者那里。arguments= 参数用于定义参数的名称,这些参数在QML中是已知的(如果使用关键字参数)。

python

    updated = Signal(str, arguments=['time'])
复制代码

你还会注意到,我们把我们的backend 对象传递给一个QML属性(也叫backend )。这使得我们刚刚实现的信号可以从QML代码中使用,并连接到一个适当的目标上。

蟒蛇

engine.rootObjects()[0].setProperty('backend', backend)
复制代码

和以前一样,我们需要在QML中实现这些要设置的属性。以前,当定义我们的属性来接收格式化的时间字符串时,我们使用了一个string 类型。这对于Backend 对象来说并不合适,因为它不是一个字符串。为了从Python中接收Backend 对象(它是一个QObject ),我们需要使用QtObject 类型。

main.qml

qml

...
property string currTime: "00:00:00"
property QtObject backend
...
复制代码

没有那么多类型。QML将Python的基本类型转换为bool,int,double,string,list,QtObjectvarvar 类型是一个通用处理程序,可以处理任何Python类型。

为了接收信号本身,我们需要定义一个Connections 对象,把它的target 作为我们的backend 属性(在QML中)。

main.qml

qml

ApplicationWindow {
    ...

    Connections {
        target: backend
    }

    ...
}

复制代码

我们现在可以在这个Connections 对象中实现任何我们喜欢的其他逻辑,以处理backend 对象上的信号。让我们创建一个信号处理程序来处理我们的updated 信号。信号处理程序会自动使用我们在Python中选择的信号名称的大写形式进行命名,前面是小写的on 。下划线和现有的大写字母会被忽略。

Python名称QML名称
mySignalonMySignal
mysignalonMysignal
my_signalonMy_signal

main.qml

qml

ApplicationWindow {
    ...

    Connections {
    target: backend

    function onUpdated(msg) {
        currTime = msg;
    }

    ...
}

复制代码

上面的代码显示了updated 信号的信号处理程序,名为onUpdated 。这个处理程序接收作为一个字符串的当前时间(命名为msg ),并将其设置到QML属性currTime 。和以前一样,设置这个属性会自动更新相关的文本标签。

如果你现在运行这个应用程序,你会看到时间的更新和以前完全一样。

如果我们愿意的话,我们可以在QML里面用Javascript的格式化代码代替Python的时间格式化,并将时间戳作为信号发送。事实上,你也可以在QML中获得时间并定义计时器

移除窗口的装饰

为了创建一个类似于桌面小工具的应用程序,你可以隐藏你的QML应用程序的窗口装饰。这将删除标题栏和关闭/最小化应用程序的按钮。然而,如果你需要,你仍然可以从任务栏上关闭窗口。对QML文件的顶部做如下修改,设置flags 属性,并将小部件定位到显示屏的右下方。

main.qml

qml

...
ApplicationWindow {
    visible: true
    width: 400
    height: 600
    x: screen.desktopAvailableWidth - width - 12
    y: screen.desktopAvailableHeight - height - 48
    title: "Clock"
    flags: Qt.FramelessWindowHint | Qt.Window
...
复制代码

该代码为窗口设置了xy ,并添加了标志Qt.FramelessWindowHint ,以使窗口无框架。Qt.Window 标志确保即使窗口是无框架的,我们仍然在任务栏上得到一个条目。运行它,你会看到我们所创建的窗口。

The final view with the updating clock and no window decorations 带有更新的时钟和无窗口装饰的最终视图

在下一个教程中,我们将通过使用图像处理、过渡和动画来扩展这个简单的时钟,建立一个全功能的模拟时钟。

完整的最终代码

下面是PySide2和PySide6的完整最终代码。

main.py

  • PySide2
  • PySide6

蟒蛇

import sys

from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QTimer, QObject, Signal

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')


class Backend(QObject):

    updated = Signal(str, arguments=['time'])

    def __init__(self):
        super().__init__()

        # Define timer.
        self.timer = QTimer()
        self.timer.setInterval(100)  # msecs 100 = 1/10th sec
        self.timer.timeout.connect(self.update_time)
        self.timer.start()

    def update_time(self):
        # Pass the current time to QML.
        curr_time = strftime("%H:%M:%S", localtime())
        self.updated.emit(curr_time)


# Define our backend object, which we pass to QML.
backend = Backend()

engine.rootObjects()[0].setProperty('backend', backend)

# Initial call to trigger first update. Must be after the setProperty to connect signals.
backend.update_time()

sys.exit(app.exec())
复制代码

蟒蛇

import sys

from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QTimer, QObject, Signal

from time import strftime, localtime

app = QGuiApplication(sys.argv)

engine = QQmlApplicationEngine()
engine.quit.connect(app.quit)
engine.load('main.qml')


class Backend(QObject):

    updated = Signal(str, arguments=['time'])

    def __init__(self):
        super().__init__()

        # Define timer.
        self.timer = QTimer()
        self.timer.setInterval(100)  # msecs 100 = 1/10th sec
        self.timer.timeout.connect(self.update_time)
        self.timer.start()

    def update_time(self):
        # Pass the current time to QML.
        curr_time = strftime("%H:%M:%S", localtime())
        self.updated.emit(curr_time)


# Define our backend object, which we pass to QML.
backend = Backend()

engine.rootObjects()[0].setProperty('backend', backend)

# Initial call to trigger first update. Must be after the setProperty to connect signals.
backend.update_time()

sys.exit(app.exec_())
复制代码

main.qml

qml

import QtQuick 2.15
import QtQuick.Controls 2.15

ApplicationWindow {
    visible: true
    width: 400
    height: 600
    x: screen.desktopAvailableWidth - width - 12
    y: screen.desktopAvailableHeight - height - 48
    title: "Clock"
    flags: Qt.FramelessWindowHint | Qt.Window
    property string currTime: "00:00:00"
    property QtObject backend

    Rectangle {
        anchors.fill: parent

        Image {
            sourceSize.width: parent.width
            sourceSize.height: parent.height
            source: "./images/background.png" srcset="https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-100 100w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-200 200w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-400 400w, https://ik.imagekit.io/mfitzp/pythonguis/tutorials/pyside-qml-qtquick-python-application/background.png?tr=w-600 600w" loading="lazy" width="564" height="1003"
            fillMode: Image.PreserveAspectCrop
        }

        Rectangle {
            anchors.fill: parent
            color: "transparent"


            Text {
                anchors {
                    bottom: parent.bottom
                    bottomMargin: 12
                    left: parent.left
                    leftMargin: 12
                }
                text: currTime  // used to be; text: "16:38:33"
                font.pixelSize: 48
                color: "white"
            }

        }

    }

    Connections {
        target: backend

        function onUpdated(msg) {
            currTime = msg;
        }
    }

}
复制代码

现在你有了你的基本QML应用程序,你应该尝试定制和改变行为。试着改变背景图片,修改文本颜色或从Python向你的应用程序发送不同(或多个)信息。

分类:
阅读
标签:
收藏成功!
已添加到「」, 点击更改