Qt 6 QML 和訳文書 P674~P700
Qt for Python
This chapter describes the PySide6 module from the Qt for Python project. You will learn how to install it and how to leverage QML together with Python.
この章はQt for PythonプロジェクトからPySide6モジュールを記述します。あなたはそれをインストールする方法と、Pythonと共にQMLも活用する方法を学びます。
Introduction
Qt for Python プロジェクトは、C++ と Qt を Python にバインドするためのツールと、完全な Python API を Qt にバインドするためのツールを提供します。これは、QtとC ++でできることはすべて、QtとPythonでも実行できることを意味します。これは、①ヘッドレスサービスからウィジェットベースのユーザーインターフェイスにまで及びます。この章では、QMLとPythonを統合する方法に焦点を当てます。現在、Qt for Pythonはすべてのデスクトッププラットフォームで利用できますが、モバイルでは使用できません。使用するプラットフォームによってPythonのセットアップは若干異なりますが、Python(https://www.python.org/)とPyPA(https://www.pypa.io/en/latest/)の環境設定ができ次第、pipを使ってQt for Pythonをインストールできます。これについては、後で詳しく説明します。Qt for PythonプロジェクトはQtにまったく新しい言語バインディングを提供するため、新しいドキュメントセットも付属しています。次のリソースは、このモジュールを探索する際に知っておくとよいでしょう。
リファレンスドキュメント:https://doc.qt.io/qtforpython/
Qt for Python wiki: https://wiki.qt.io/Qt_for_Python
注意事項:https://wiki.qt.io/Qt_for_Python/Considerations
Qt for Pythonバインディングは、シボケンツールを使用して生成されます。時々、何が起こっているのかを理解するためにそれについても読むことは興味深いかもしれません。Shibokenに関する情報を見つけるための好ましいポイントは、リファレンスドキュメント(https://doc.qt.io/qtforpython/shiboken6/index.html)です。独自のC ++コードをPythonおよびQMLと混在させたい場合は、シボケンが必要なツールです。
①
ヘッドレスサービスっていう言葉はありますか?
ChatGPT
はい、"ヘッドレスサービス"(Headless Service)という言葉は存在します。これは、クライアントとサーバーの間で通信を行う際に、UI(ユーザーインターフェース)を持たないサービスやアプリケーションを指す用語です。
通常、Webサーバーやマイクロサービスなどのバックエンドのコンポーネントは、ユーザーインターフェースの表示や操作を行いません。そのような場合、ヘッドレスサービスとして知られるサービスを利用することがあります。ヘッドレスサービスは、API(Application Programming Interface)を介して他のシステムやクライアントアプリケーションとやり取りし、データの処理や結果の提供を行います。
ヘッドレスサービスの例としては、コンテンツ管理システム(CMS)や電子商取引プラットフォームなどがあります。これらのサービスは、データの作成や更新、取得を行いながら、フロントエンドのUI表示は別のシステムやクライアントが担当します。ヘッドレスサービスを使用することで、バックエンドとフロントエンドの独立性が高まり、柔軟なシステム構築が可能となります。
したがって、"ヘッドレスサービス"は、ユーザーインターフェースを持たず、APIを通じてデータの処理や提供を行うサービスやアプリケーションを指す用語として使用されます。
TIP
この章を通して私たちはPython3.7を使います。
Qt for Pythonはpyside6の名の元、pipを使ってPyPAを通して利用可能です。下記の例では私たちがQt for Pythonの最新バージョンをインストールする仮想環境をセットアップします。
mkdir qt-for-python
cd qt-for-python
python3 -m venv .
source bin/activate
(qt-for-python) $ python --version
Python 3.9.6
環境がセットアップされるとき、私たちはpipを使ってpyside6をインストールすることを続けます。
(qt-for-python) $ pip install pyside6
Collecting pyside6
Downloading [ ... ] (60.7 MB)
Collecting shiboken6==6.1.2
Downloading [ ... ] (1.0 MB)
Installing collected packages: shiboken6, pyside6
Successfully installed pyside6-6.1.2 shiboken6-6.1.2
インストールの後、私たちは相互作用のPythonプロンプトからHello Worldエグザンプルを実行することでそれをテストします。
(qt-for-python) $ python
Python 3.9.6 (default, Jun 28 2021, 06:20:32)
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more infor
>>> from PySide6 import QtWidgets
>>> import sys
>>> app = QtWidgets.QApplication(sys.argv)
>>> widget = QtWidgets.QLabel("Hello World!")
>>> widget.show()
>>> app.exec()
0
>>>
The example results in a window such as the one shown below. To end the program, close the window.
ウィンドウ内のエグザンプルの結果は例えば下記に示された一つです。プログラムを終わらせるため、ウィンドウを閉じます。
Building an Application
この章では私たちはどのようにPythonとQMLを組み合わせることができるのかを見ます。二つの世界をつなぐ最も自然な方法は、C++とQMLで行うこと同様に、例えば、QML内の表現とPythonのロジックを実装することです。これを行うために、QMLとPythonを一つのプログラムに組み合わせる方法を理解する必要があります。下記のサブセクションでは、私たちはこれがどのように行われるのかを見ます。単純なものをはじめて、Qtアイテムモデルを通してQMLへPythonモジュールの能力を公開する例へ進みます。
Running QML from Python
まさに最初のステップは下記に示されたHello World QMLプログラムを①ホストすることができるPythonプログラムをつくることです。
プログラミングでホストというと、どのような意味を持ちますか。
ChatGPT
プログラミングの文脈では、"ホスト"(host)という用語は複数の意味を持つことがあります。以下にいくつかの一般的な意味を示します。
ホストコンピュータ(Host Computer): プログラムやアプリケーションが実行される物理的なコンピュータやサーバーを指します。例えば、ウェブサイトをホストするためには、ウェブサーバーがホストコンピュータとして機能します。
ホスト言語(Host Language): プログラミング言語の文脈で使用される用語で、他の言語や環境に埋め込まれる形で使用される言語を指します。たとえば、C言語は、他のプログラミング言語(ホスト言語)に埋め込まれて使用されることがあります。
ホスト環境(Host Environment): プログラムが実行されるための実行環境やフレームワークを指します。例えば、JavaScriptの場合、ウェブブラウザがホスト環境となり、JavaScriptプログラムが実行されます。
ホストアプリケーション(Host Application): プラグインや拡張機能などが組み込まれる主要なアプリケーションを指します。プラグインがホストアプリケーションと連携して機能する場合、ホストアプリケーションはプラグインをホストします。
ここでは、3?
これらは一般的な意味であり、文脈によって異なる場合もあります。そのため、具体的な文脈に応じて、"ホスト"の意味を適切に理解する必要があります。
import QtQuick
import QtQuick.Window
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello Python World!")
}
これを行うために、私たちはQtGuiモジュールからのQGuiApplicationによって提供されるQtのメインループを必要とします。私たちはQtQmlモジュールからQQmlApplicationEngineも必要とします。QMLアプリケーションエンジンへソースファイルへのリファレンスを渡すために、私たちはQUrlクラスをQtCoreモジュールからも必要とします。下記のコード内では、QMLプロジェクトのためのQt Creatorによって造られた①ボイラープレートのC++コードの機能をエミュレートしています。それはアプリケーションオブジェクトを制し、そしてQMLアプリケーションエンジンを作ります。その時、QMLを読込、もしルートオブジェクトが作られたらQMLはチェックすることによって読み込まれることを保証します。最後に、アプリケーションオブジェクトのexecメソッドによって返される値を返し、ループを抜けます。
これらは一般的な意味であり、文脈によって異なる場合もあります。そのため、具体的な文脈に応じて、"ホスト"の意味を適切に理解する必要があります。
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QUrl
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
Executing the example results in a window with the title Hello Python World.
例を実行することはHello Python Worlldというタイトルのウィンドウが結果になります。
TIP
例は、main.qmlソースファイルを持つディレクトリから実行されることを想定しています。あなたは__file__変数を使って実行されているPythonファイルの位置を決定することができます。これはこのブログポスト内で示されている通りPythonファイルに関連するQMLファイルを位置づけるために利用することができます。
Exposing Python Objects to QML
PythonとQML間で情報を共有する最も簡単な方法はPythonオブジェクトをQMLへ公開することです。これはQQmlApplicationEngineを通して、context propertyを登録することによって行われます。私たちがそれを行う前に、私たちは公開するオブジェクトをもつためにクラスを定義する必要があります。Qtクラスは私たちが使うことができるようになりたい多くの機能を持っています。これらは、signals, slotsそしてpropertiesです。この最初の例では、私たちは基本的なペアのシグナルとスロットに制限します。残りはさらなるエグザンプルの中でカバーされます。
Signals and Slots
NumberGeneratorクラスで始めます。updateNumberと呼ばれるコンストラクタ、メソッドを持ち、nextNumberと呼ばれるシグナルを持ちます。あなたがupdateNumberと呼ぶとき、signal nextNumberが新しいランダムの数で発行されるという考えです。あなたは下記のクラスのためのコードを見ることができますが、最初に私たちは詳細に目を向けます。とりわけ私たちは、QObject._init_ from our constructorを確実に呼びます。これはとても重要で、それなくしてエグザンプルは動かないからです。PySide6.QtCoreモジュールからSignalクラスのインスタンスをつくることによってシグナルを宣言します。シグナル引数name, number, は引数内で定義されます。最後に、私たちは@Slotデコレータを使ってupdateNumberメソッドをデコレートし、そのようにしてそれをスロットへ変えます。Qt for Python内で引き起こすことが可能な概念はまったくなく、全てのコール可能なメソッドはslotsでなければなりません。updateNumberメソッド内で私たちはnextNumberシグナルをemitメソッドを使って発行します。シグナルは呼び出し可能な関数である代わりにオブジェクトによって表現されるので、これはQMLあるいはC++からのそれを行うためのシンタックスとはちょっと異なっています。
import random
from PySide6.QtCore import QObject, Signal, Slot
class NumberGenerator(QObject):
def __init__(self):
QObject.__init__(self)
nextNumber = Signal(int, arguments=['number'])
@Slot()
def giveNumber(self):
self.nextNumber.emit(random.randint(0, 99))
次にあげるのは私たちがQMLとPythonを早くから組み合わせるためのボイラープレートコードでつくられたクラスを組み合わせることです。①これは次のエントリポイントコードを与えてくれます。興味深い行は私たちがNumberGeneratorを最初にインスタンス化するところです。このオブジェクトはQMLエンジンのrootContextのsetContextPropertyメソッドを使ってQMLに公開されます。これはnumberGeneratorという名前でオブジェクトをグローバル変数としてQMLへ公開します。
①エントリポイントとは
ChatGPT
エントリポイント(Entry Point)とは、プログラムの実行が開始される場所や、外部からアクセスされるメインのポイントを指します。プログラムの実行が始まる際、エントリポイントは最初に実行されるコードや関数のことを指します。
エントリポイントは、プログラムの起動や実行フローの制御を担当します。多くのプログラミング言語やフレームワークでは、エントリポイントの特定の命名規則や構造が定義されています。プログラムが実行されるとき、エントリポイントが呼び出され、それによって他のコードや関数が実行されるようになります。
具体的な例としては、以下のような場合があります。
C言語でのエントリポイント: C言語では、通常、main関数がエントリポイントとして機能します。プログラムが実行されると、最初にmain関数が呼び出され、プログラムの実行が開始されます。
cCopy codeint main() {
// プログラムの実行フローが始まる
// ...
return 0;
}
Javaでのエントリポイント: Javaでは、エントリポイントはmainメソッドとして知られています。Javaプログラムが実行されると、mainメソッドが呼び出され、プログラムの実行が開始されます。
javaCopy codepublic class Main {
public static void main(String[] args) {
// プログラムの実行フローが始まる
// ...
}
}
Webアプリケーションでのエントリポイント: Webアプリケーションでは、一般的に特定のURLやエンドポイントがエントリポイントとして機能します。クライアントからのリクエストが特定のURLに到達すると、そのURLに関連付けられたコードやハンドラが実行されます。
エントリポイントはプログラムの制御を開始する重要なポイントであり、プログラムの挙動や機能に大きな影響を与えます。プログラムの実行フローを理解し、適切にエントリポイントを設定することが重要です。
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtCore import QUrl
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
number_generator = NumberGenerator()
engine.rootContext().setContextProperty("numberGenerator",
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
QMLコードで続けるために、ボタンとラベルで構成するQt Quick Controls2 ユーザーインターフェースを作ったということがわかります。ボタンのonClickedハンドラ内では、numberGenerator.updateNumber()関数が呼ばれます。これはPython側で生成されるオブジェクトのスロットです。QMLの外部で生成されたオブジェクトからのシグナルを受け取るためには、私たちはConnections要素を使う必要があります。これは存在しているターゲットへシグナルハンドラを取り付けるようにするものです。
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello Python World!")
Flow {
Button {
text: qsTr("Give me a number!")
onClicked: numberGenerator.giveNumber()
}
Label {
id: numberLabel
text: qsTr("no number")
}
}
Connections {
target: numberGenerator
function onNextNumber(number) {
numberLabel.text = number
}
}
}
Properties
シグナルとスロットだけに依存する代わりに、QMLへステート(状態)を公開する一般的な方法は、プロパティを通す事です。プロパティはセッター、ゲッターと、通知シグナルの組み合わせです。セッターは任意であり、私たちは読取専用のプロパティも持つことができます。これを試してみるに、私たちは最後のエグザンプルからNumberGeneratorを更新し、プロパティを基本としたバージョンにします。二つのプロパティを持ちます。number, 最後のランダムナンバーをもつ読取り専用プロパティです。maxNumber,戻り値として返せる最大値を持つ読書可能プロパティです。私たちがプロパティの詳細に飛び込む前に、このための基本的なPythonクラスを作ります。関連するゲッターとセッターを構成しますが、Qtのシグナルはありません。実際に重要なのは、ここで唯一のQtの部品はQObjectからの継承であるということです。メソッドの名前さえもPythonスタイルで、キャメルケースの代わりにアンダースコアを用います。__set_numberメソッドの始まりの__に注意して下さい。これはプライベートメソッドであることを意味しています。だからnumber propertyが読取専用であるときでさえ、私たちはセッターを提供します。私たちはそれを公開しません。これは値を変更するときにアクションをとることができます。(例えば、notificationシグナルを発行することです。)
class NumberGenerator(QObject):
def __init__(self):
QObject.__init__(self)
self.__number = 42
self.__max_number = 99
def set_max_number(self, val):
if val < 0:
val = 0
if self.__max_number != val:
self.__max_number = val
if self.__number > self.__max_number:
self.__set_number(self.__max_number)
def get_max_number(self):
return self.__max_number
def __set_number(self, val):
if self.__number != val:
self.__number = val
def get_number(self):
return self.__number
プロパティを定義するために、私たちは、Signal, Slot,そして①PySide2.QtCoreからPropertyの概念をインポートする必要があります。完全な例では、よりたくさんのインポートがありますが、これらはプロパティに関連するものです。
①PySide6の間違い?
from PySide6.QtCore import QObject, Signal, Slot, Property
今私たちは最初のプロパティを定義する準備が整っています。私たちはシグナル numberChangedを宣言することからスタートし、私たちはそれから__set_numberメソッド内で値が変更されるときにそのシグナルが発行されるように呼び出します。その後、残されている全ては、Propertyオブジェクトを生成することです。Propertyコンストラクタはこの場合に3つの引数を取ります。type(int), getter(get_number)そしてそして、(notify=numberChanged)と名付けられた引数として渡される通知シグナルです。ゲッターはPythonの名前を持ち、(キャメルケースよりアンダースコアをつかっていて、)パイソンから値を読むために使われることに注意してください。QMLのためには、プロパティの名前は、ナンバーが使われます。
class NumberGenerator(QObject):
# ...
# number
numberChanged = Signal(int)
def __set_number(self, val):
if self.__number != val:
self.__number = val
self.numberChanged.emit(self.__number)
def get_number(self):
return self.__number
number = Property(int, get_number, notify=numberChanged)
これは次のプロパティ、maxNumberを導きます。これは読み書き可能プロパティで、numberプロパティに対して行った全てだけでなく、私たちはセッターを必要とします。
最初に私たちはmaxNumberChangedシグナルを宣言します。この時、@Signalデコレータを、Signalオブジェクトを生成する代わりに使います。私たちはセッタースロットsetMaxNumberをQt名で(キャメルケース)も提供し、単純にPython名でのゲッターにのそばにあるPythonのメソッドset_max_numberを呼ぶだけです。また、セッターは値が更新されるとchangeシグナルを発行します。最後に、引数として、getter, setter,そして通知シグナルを取るプロパティオブジェクトをインスタンス化することによって読込書き込みプロパティへコード片をまとめて組み立てます。
class NumberGenerator(QObject):
# ...
# maxNumber
@Signal
def maxNumberChanged(self):
pass
@Slot(int)
def setMaxNumber(self, val):
self.set_max_number(val)
def set_max_number(self, val):
if val < 0:
val = 0
if self.__max_number != val:
self.__max_number = val
self.maxNumberChanged.emit()
if self.__number > self.__max_number:
self.__set_number(self.__max_number)
def get_max_number(self):
return self.__max_number
maxNumber = Property(int, get_max_number, set_max_number, n
今私たちは現在のrandom number, number, そしてmaximum random number, maxNumberのためにプロパティを持ちます。残されている全ては新しいrandomナンバーを作るためのスロットです。新しいランダムナンバーを単純にセットし、updateNumberを呼びます。
class NumberGenerator(QObject):
# ...
@Slot()
def updateNumber(self):
self.__set_number(random.randint(0, self.__max_number))
Finally, the number generator is exposed to QML through a root context property.
最後に、numberジェネレータはルートコンテキストプロパティを通してQMLに公開されます。
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
number_generator = NumberGenerator()
engine.rootContext().setContextProperty("numberGenerator",
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
QMLでは、私たちはnumberGeneratorオブジェクトのmaxNumberプロパティだけでなく、数にバインドできます。ボタンのonClickedハンドラ内に私たちは新しいランダムナンバーを発生させるためのupdateNumberメソッドを、スライダーのonValueChangedハンドラ内には、setMaxNumberメソッドを使っているmaxNumberプロパティをセットします。これはJavaScroptから直接プロパティを変更することは、プロパティへのバインディングを破壊するためです。明示的にセッターメソッドを使うことにより、これは避けられます。
import QtQuick
import QtQuick.Window
import QtQuick.Controls
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello Python World!")
Column {
Flow {
Button {
text: qsTr("Give me a number!")
onClicked: numberGenerator.updateNumber()
}
Label {
id: numberLabel
text: numberGenerator.number
}
}
Flow {
Slider {
from: 0
to: 99
value: numberGenerator.maxNumber
onValueChanged: numberGenerator.setMaxNumber(va}
}
}
}
Exposing a Python class to QML
今まで、私たちはあるオブジェクトPythonを初期化し、QMLで利用可能にするrootContextのうちsetContextPropertyメソッドを使いました。QMLからオブジェクトを生成することができるということは、QMLからオブジェクトのライフサイクルを越えてよりよいコントロールをすることができます。これを可能にするには、私たちはそのクラスを、そのオブジェクトの代わりにQMLに公開する必要があります。QMLに公開されているクラスは生成されている場所によって影響をうけません。クラス定義へは全く変更が必要ありません。しかし、setContextPropertyを呼ぶ代わりに、qmlRegisterType関数が使われます。この関数はPySide2.QtQmlモジュールからきていて、5つの引数を取ります。
A reference to the class, NumberGenerator in the example below.
A module name, 'Generators' .
A module version consisting of a major and minor number, 1 and 0 meaning 1.0 .
The QML name of the class, 'NumberGenerator'
クラスへのリファレンス、下記の例の中のNumberGenerator
モジュール名'Generators'
メジャーとマイナーのナンバーを構成しているモジュールバージョン。1と0は、1.0を意味します。
クラスのQML名,'NumberGenerator'
import random
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterTyp
from PySide6.QtCore import QUrl, QObject, Signal, Slot
class NumberGenerator(QObject):
def __init__(self):
QObject.__init__(self)
nextNumber = Signal(int, arguments=['number'])
@Slot()
def giveNumber(self):
self.nextNumber.emit(random.randint(0, 99))
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qmlRegisterType(NumberGenerator, 'Generators', 1, 0, 'Numbe
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
QMLでは、私たちはimport moduleをする必要があります。例えば、Generators 1.0とそれからNumberGenerator{…}としてクラスを初期化します。インスタンスは今や他のQML要素のように動きます。
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import Generators
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello Python World!")
Flow {
Button {
text: qsTr("Give me a number!")
onClicked: numberGenerator.giveNumber()
}
Label {
id: numberLabel
text: qsTr("no number")
}
}
NumberGenerator {
id: numberGenerator
}
Connections {
target: numberGenerator
function onNextNumber(number) {
numberLabel.text = number
}
}
}
A Model from Python
PythonからQMLへ公開されるより興味深いタイプのオブジェクトやクラスの一つはアイテムモデルです。これらはいろいろなビューや、動的にモデルコンテンツからユーザーインターフェースを組み立てるためのリピーター要素で使われるものです。このセクションでは、CPU読みこみ(そしてそれ以上に)、psutil,そしてそれをカスタムで作成されたアイテムモデル(CpuLoadModel)と呼ばれるモデルを通してQMLへ公開するための存在しているPythonユーティリティを取ります。あなたは下記で動作中のプログラムを見ることができます。
QMLでリピーターという言葉が出てくるのですが、どのような意味ですか。
ChatGPT
QML(Qt Meta-Object Language)は、Qtフレームワークで使用されるDeclarative UI(宣言的なUI)言語です。QMLは、GUI(グラフィカルユーザーインターフェース)を作成するための柔軟で強力な手段を提供します。
QMLにおける「リピーター(Repeater)」は、特定のアイテムやコンポーネントを複数回繰り返し表示するために使用されるコントロールです。リピーターは、データモデルやリストなどの要素の数に基づいて、同じコンポーネントの複数のインスタンスを自動的に生成します。
リピーターを使用することで、データの一覧や項目の表示、ダイナミックなUIの作成など、重複する要素の処理を簡素化できます。リピーターは、QML内でループや繰り返し処理を行うための強力なツールとして活用されます。
以下に、QMLでのリピーターの使用例を示します:
qmlCopy codeRepeater {
model: 10 // 10回繰り返す
delegate: Rectangle {
width: 100
height: 100
color: index % 2 === 0 ? "red" : "blue" // 偶数回目は赤、奇数回目は青
}
}
上記の例では、Repeaterコンポーネントが10回繰り返し、各繰り返しでRectangleコンポーネントが生成されます。delegateプロパティには、生成される各インスタンスの外観や振る舞いを定義するコンポーネントが指定されます。
このように、リピーターを使用することで、簡潔なコードで複数の要素を反復的に生成することができます。
TIP
psutil(process and system utilities)はパイソンで、(CPU, memory, disks, network, sensors)のようなシステムゆーてぃりゼーションと、実行プロセス上の情報を取得するためのクロスプラットフォームライブラリです。あなたはpsutilを、pip install psutilを使ってインストールできます。私たちは、psutil.cpu_percent関数を使います。(ドキュメントは(https://psutil.readthedocs.io/en/latest/#psutil.cpu_percent))で、CPUぉー路を一秒ごとに、コアごとにサンプルします。サンプリングを処理するために、QTimerを使います。これの全てはQAbstractListModelであるCpuLoadModelを通して公開されます。
アイテムモデルは面白いです。それらは2次元のデータセットを表現することができ、もしQAbstractItemModelを利用するのであれば、ネストしたデータセットでさえも表現することができます。QAbstractListModelはリストのアイテムを表現することができ、1次元のデータセットです。ツリーをつくることで、ネストされたセットのリストを実装することができますが、一つの段を作るだけです。QAbstractListModelを実装するために、rowCountとdataメソッドを実装する必要があります。rowCountはpsutil.cpu_countメソッドを使うCPUコアの数を返します。Qt.DisplayRoleだけをサポートします。これはQMLからデリゲート内で表示するために参照するときに得るものに対応しています。そのモデルのためのコードをみると、実際のデータは__cpu_loadリスト内に格納されていることがわかります。もし有効なリクエストがデータになされると、たとえば、行、列、そしてロールが正確であると、__cpu_loadリストから正しい要素が返されてきて、そうでなければNoneを返します。NoneはQtサイドでは初期化されなかったQVariantに対応するものです。update timerがタイムアウトするごとに、__updateメソッドが呼ばれます。こちらでは、_cpu_loadリストが更新されますが、全てのデータが変更されることを指示するdataChangedシグナルも発行します。アイテムの数が変化したかもしれないということも意味するので、modelResetはしません。最後に、CpuLoadModelはPsUtilsモジュール内に登録された型であるQMLへ公開されます。
import psutil
import sys
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, qmlRegisterTyp
from PySide6.QtCore import Qt, QUrl, QTimer, QAbstractListModel
class CpuLoadModel(QAbstractListModel):
def __init__(self):
QAbstractListModel.__init__(self)
self.__cpu_count = psutil.cpu_count()
self.__cpu_load = [0] * self.__cpu_count
self.__update_timer = QTimer(self)
self.__update_timer.setInterval(1000)
self.__update_timer.timeout.connect(self.__update)
self.__update_timer.start()
# The first call returns invalid data
psutil.cpu_percent(percpu=True)
def __update(self):
self.__cpu_load = psutil.cpu_percent(percpu=True)
self.dataChanged.emit(self.index(0,0), self.index(self.
def rowCount(self, parent):
return self.__cpu_count
def data(self, index, role):
if (role == Qt.DisplayRole and
index.row() >= 0 and
index.row() < len(self.__cpu_load) and
index.column() == 0):
return self.__cpu_load[index.row()]
else:
return None
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
qmlRegisterType(CpuLoadModel, 'PsUtils', 1, 0, 'CpuLoadMode
engine.load(QUrl("main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec())
QMLサイドでは、私たちはListViewをCPU読込を見せるために使います。モデルはモデルプロパティと結びついています。モデル内のそれぞれのアイテムのために、デリゲートアイテムが初期化されます。このケースでは、green barつきのRectangleと現在の読込を表示するText要素とを意味しています。
import QtQuick
import QtQuick.Window
import PsUtils
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("CPU Load")
ListView {
anchors.fill: parent
model: CpuLoadModel { }
delegate: Rectangle {
id: delegate
required property int display
width: parent.width
height: 30
color: "white"
Rectangle {
id: bar
width: parent.width * delegate.display / 100.0
height: 30
color: "green"
}
Text {
anchors.verticalCenter: parent.verticalCenter
x: Math.min(bar.x + bar.width + 5, parent.width
text: delegate.display + "%"
}
}
}
}
Limitations
制限
今の時点では、簡単に利用できないいくつかの物事があります。それらのうちの一つは、あなたは簡単にPythonを使ってQMLプラグインを作ることができないということです。代わりにあなたがPython QML "modules"をPythonプログラムの中にインポートし、QMLからそれらをインポートすることが可能になるようにqmlRegisterTypeを使う必要があります。
Summary
この章では、Qt for PythonからのPySide6モジュールを見ました。インストールを手短に見た後、Qtの概念がPythonからどのように利用されているのかに焦点を当てました。これはスロット、シグナルそしてプロパティを含んでいました。私たちは基本的なリストモデルも見ましたし、PythonからQMLのPythonオブジェクトとクラスの両方を公開する方法を見ました。