And now we will go deeper into the work with Qt using PyQt5, taking advantage of modern Qt features. By such possibilities I mean QtQuick and QML. PyQt5 allows you to use Qt classes that can process QML code, and therefore you can write an interface to QML, and also send signals to the QML layer and invoke slots of objects inherited from QObject from the QML layer.
To get meet with such possibilities of PyQt5, we will write a program that implements the following tasks:
- The program interface should be written in QML
- A class inherited from QObject and written in python must be implemented, with which we will interact from QML
- An application using this class will need to add and subtract integers
Appearance of the application should look like this:
Project structure
There will be only two files in the project:
- main .py - File application in python, there will also be a class for calculations
- main.qml - Interface file on QML
Signals in PyQt5
The signal signature in the general case will look like this:
PyQt5.QtCore.pyqtSignal( types [, name [, revision=0 [, arguments=[] ]]])
Create one or more overloaded unbound signals as a class attribute.
| Parameters: | *
types
– the types that define the C++ signature of the signal. Each type may be a Python type object or a string that is the name of a C++ type. Alternatively each may be a sequence of type arguments. In this case each sequence defines the signature of a different signal overload. The first overload will be the default.
name
– the name of the signal. If it is omitted then the name of the class attribute is used. This may only be given as a keyword argument.
revision
– the revision of the signal that is exported to QML. This may only be given as a keyword argument.
*
arguments
– the sequence of the names of the signal’s arguments that is exported to QML. This may only be given as a keyword argument.
|
Slots in PyQt5
To define slots in PyQt5, a special decorator is used.
PyQt5.QtCore.pyqtSlot( types [, name [, result [, revision=0 ]]])
| Parameters: | *
types
– the types that define the C++ signature of the slot. Each type may be a Python type object or a string that is the name of a C++ type.
name
– the name of the slot that will be seen by C++. If omitted the name of the Python method being decorated will be used. This may only be given as a keyword argument.
revision
– the revision of the slot that is exported to QML. This may only be given as a keyword argument.
*
result
– the type of the result and may be a Python type object or a string that specifies a C++ type. This may only be given as a keyword argument.
|
main .py
from PyQt5.QtGui import QGuiApplication from PyQt5.QtQml import QQmlApplicationEngine from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot class Calculator(QObject): def __init__(self): QObject.__init__(self) # Signal sending sum # Necessarily give the name of the argument through arguments=['sum'] # Otherwise it will not be possible to get it up in QML sumResult = pyqtSignal(int, arguments=['sum']) subResult = pyqtSignal(int, arguments=['sub']) # Slot for summing two numbers @pyqtSlot(int, int) def sum(self, arg1, arg2): # Sum two arguments and emit a signal self.sumResult.emit(arg1 + arg2) # Slot for subtraction of two numbers @pyqtSlot(int, int) def sub(self, arg1, arg2): # Subtract arguments and emit a signal self.subResult.emit(arg1 - arg2) if __name__ == "__main__": import sys # Create an instance of the application app = QGuiApplication(sys.argv) # Create QML engine engine = QQmlApplicationEngine() # Create a calculator object calculator = Calculator() # And register it in the context of QML engine.rootContext().setContextProperty("calculator", calculator) # Load the qml file into the engine engine.load("main.qml") engine.quit.connect(app.quit) sys.exit(app.exec_())
main.qml
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.2 ApplicationWindow { visible: true width: 640 height: 240 title: qsTr("PyQt5 love QML") color: "whitesmoke" GridLayout { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.margins: 9 columns: 4 rows: 4 rowSpacing: 10 columnSpacing: 10 Text { text: qsTr("First number") } // Input field of the first number TextField { id: firstNumber } Text { text: qsTr("Second number") } // Input field of the second number TextField { id: secondNumber } Button { height: 40 Layout.fillWidth: true text: qsTr("Sum numbers") Layout.columnSpan: 2 onClicked: { // Invoke the calculator slot to sum the numbers calculator.sum(firstNumber.text, secondNumber.text) } } Text { text: qsTr("Result") } // Here we see the result of sum Text { id: sumResult } Button { height: 40 Layout.fillWidth: true text: qsTr("Subtraction numbers") Layout.columnSpan: 2 onClicked: { // Invoke the calculator slot to subtract the numbers calculator.sub(firstNumber.text, secondNumber.text) } } Text { text: qsTr("Result") } // Here we see the result of subtraction Text { id: subResult } } // Here we take the result of sum or subtracting numbers Connections { target: calculator // Sum signal handler onSumResult: { // sum was set through arguments=['sum'] sumResult.text = sum } // Subtraction signal handler onSubResult: { // sub was set through arguments=['sub'] subResult.text = sub } } }
Спасибо. Все очень доходчиво!
Подскажите как закидывать компоненты из python на форму qml? Я хочу график matplotlib закинуть на page qml
Вот сейчас Вы меня конкретно загрузили этим вопросом. Не работал с этой библиотекой, но я глянул, что есть сети по этому поводу и кое-какие мысли уже есть у меня. Но надо ещё самому разобраться, что можно придумать. Сегодня/завтра после работы гляну, и потом отвечу Вам, какие мысли у меня будут.
Но вообще, всё скорее всего должно сводиться к множественному наследованию от QQuickPaitedItem и класса библиотек matplotlib.
Спасибо, Вам, за отклик. У меня существует готовое решение с использованием pyqt5 + matplotlib. Где я просто на форму кидаю виджет, а виджет есть график matplotlib. Теперь хотелось бы интерфейс приятный сделать. Вчера потратил весь день на Qt Charts и осознал, что он очень не очень. Ощущение, что студенты на коленках делали.
почему не выполняется код после вызова слота?
Я вот сейчас банальность скажу, но у меня всё работало. Так что даже и не знаю, надо на код смотреть, что ещё у вас добавлено или отсутствует из библиотек.
P/S/ Извините, вы сейчас всё подряд пробуете или работаете именно с Python?
Изменив математические действия столкнулся с проблемой - не выводит десятичные дроби
Проблема должна быть в описании слота, там целочисленные аргументы
В вашем случае, я думаю, так должно быть
заменил, та же картина, причем в python я вывожу переменную 11.5, а в qml вижу 11
Вот это ещё замените
на вот это
Круто! Немного начинает проясняться что к чему. Спасибо.
А можно ли из QML сделать привязку свойства к свойству пайтоновского объекта? Ну, т.е. , например, у нашего объекта Calculator обхвялем свойства sumresult и subresult c с декоратором @pyqtProperty, а в QMLе делаем типа такого: