📜 ⬆️ ⬇️

PyQt4 and QML

More recently, the developers of the Qt Software framework have delighted us with the advent of GUI, an alternative to the standard, with its own, rather simple, markup language - QML.
A QML bundle with the main program is a Qt Declarative module. Starting from version 4.7 - PyQt4 supports this module.
QML is much simpler and more flexible than the main GUI, besides being a programming language, as it allows writing functions in javascript. While Python is a fairly simple and flexible interpreted language.


Let's start


First QML form. It is fully ready and, at the same time, efficient, since, when the program is started, errors in it do not stop its operation. Some parts of the code will be covered later.

import Qt 4.7 Rectangle { //  signal wantquit property int qwX: 0; property int qwY: 0 property int owX: 0; property int owY: 0 property bool first: true /*      */ function updateMessage(text) { messageText.text = text } anchors.fill: parent; color: "black" //  Text { id: messageText; anchors.centerIn: parent; color: "white" } //    MouseArea { anchors.fill: parent onClicked: { //     python messageText.text = someone.some_id first = true } onPositionChanged: { owX = first? mouseX : owX owY = first? mouseY : owY first = false qwX+=mouseX-owX qwY+=mouseY-owY //  main.form_move(qwX, qwY) } onDoubleClicked: { //  wantquit() } } } 

')
In this case, the form will be saved with the name "form.qml", in the same directory as the python file.
Now output this form using PyQt. For this, the QtDeclarative module has an element QDeclarativeView. It inherits QWidget properties and functions, so it can be either a separate window or embedded as a child, respectively, and has a connect method.

 from PyQt4 import QtCore, QtGui, Qt, QtDeclarative import sys app = QtGui.QApplication(sys.argv) #  QML  view = QtDeclarative.QDeclarativeView() view.setSource(QtCore.QUrl('form.qml')) view.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView) view.setGeometry(100, 100, 400, 240) view.show() sys.exit(app.exec_()) 


The result was a stripped-down qmlviewer.
Further, for convenience and efficiency of some methods, create a class that inherits QDeclarativeView, slightly change the appearance of the form and, as is appropriate for downward programming, create additional functions of the “stub” that will be called when the class is initialized.
At startup, a black rectangle will appear. (Since there is no window frame, it can be closed only from the taskbar).

 from PyQt4 import QtCore, QtGui, Qt, QtDeclarative class I_will_be_form(QtDeclarative.QDeclarativeView): def __init__(self, parent=None): QtDeclarative.QDeclarativeView.__init__(self, parent) #   self.setWindowFlags(Qt.Qt.FramelessWindowHint) self.setSource(QtCore.QUrl.fromLocalFile('form.qml')) self.setResizeMode(QtDeclarative.QDeclarativeView.SizeRootObjectToView) self.setGeometry(100, 100, 400, 240) self.signal_func_Qml() self.signalThis() self.slot() self.prop() def signal_func_Qml(self): print "Qml's signal" def signalThis(self): print "Signal of PyQt" def slot(self): print "Property" def prop(self): print "Slot " import sys app = QtGui.QApplication(sys.argv) Iwbf = I_will_be_form() Iwbf.show() sys.exit(app.exec_()) 


QML Access


Let's start with filling in the signal_func_Qml function. QDeclarativeView has access to the structure of the QML file that it has loaded using the rootObject () method. This method returns the root object. Accordingly, we can manipulate the functions and signals of this file. We cannot directly assign a property to a QML element. (Even if they could, it would be wiser to do this through a function).
So, in the QML file, we already have a signal called wantquit, which is sent by double-clicking on the space of the root widget. And the updateMessage function, which writes the text passed to it into a text widget.

 def signal_func_Qml(self): print "Qml's signal" root = self.rootObject() #(1) root.wantquit.connect(app.quit) #(2) root.updateMessage(QtCore.QString('From root')) #(3) 


This is how the function will be filled. In the line with number (1), we get the root object to the local variable root, in line (2) we attach the function to terminate the application to the wantquit signal, in line (3) we execute the updateMessage function. It is worth noting that the string values ​​that are passed to the QML file must be transferred to the QString type, because the usual type of str QML file will not accept, with the same numeric formats there are no such problems.
Similarly, the signal sent by the Python file class can be processed. To do this, we write in the class I_will_be_form, the signal inited (before the initialization of the class and at the same level with it):

 inited = QtCore.pyqtSignal(str) def __init__(self, parent=None): ...... 


Also fill the signal function:

 def signalThis(self): print "Signal of PyQt" root = self.rootObject() #(1) self.inited.connect(root.updateMessage) #(2) self.inited.emit(QtCore.QString("I'm ready!")) #(3) 


In line (1) we get the root object again (since in the previous function it was in a local variable). In line (2) we bind the signal inited the function of the QML file updateMessage. Accordingly, the text that sends the signal will be passed to the function as a parameter. In line (3) we send a signal with the text “I'm ready!”. (Again, do not forget to transfer to QString, although it is not necessary here, but still it would be nice to be safe once again).

Access to PyQt


In addition to accessing from PyQt in QML, there is also a reverse possibility. Let's start c filling the slot function.

 def slot(self): print "Property" self.engine().rootContext().setContextObject(self) #(1) self.engine().rootContext().setContextProperty('main', self) #(2) 


In both lines, we open access from the QML to the PyQt object. Only in the first case, the functions of the self object (here it is I_will_be_form) become the functions of the root QML widget, and the functions of the I_will_be_form class are accessed by their names. In the second case, the class I_will_be_form becomes the root widget with the identifier main, and the access to the functions is respectively main. <Function name>, which eliminates name conflicts and simplifies code understanding. But access is still not open to all functions.
Initially, QML is adapted for C ++, which is a strongly typed language, and its classes have such concepts as private, public, and protected. In Python, there is neither typing nor these concepts. PyQt developers had to fix this problem. So, we will describe a certain function all in the same I_will_be_form class:

 @QtCore.pyqtSlot(int, int) #(1) def form_move(self, x, y): self.move(x, y) 


The function moves the QDeclarativeView window according to the passed x and y coordinates. Now we turn our attention to line (1), it makes our function a slot, which allows access to this function from QML, but in this case it cannot return a value.
As a result, the QML code fragment in the onPositionChanged block starts to make sense, it passes the values ​​of the form_move function so that it moves the window, and at start-up, by holding the mouse button on the rectangle, you can move it.
Now fill the prop function.

 def prop(self): print "Slot" self.engine().rootContext().setContextProperty('someone', so) 


We will also additionally describe another class Someone before I_will_be_form and immediately initialize it in the global variable so.

 class Someone(QtCore.QObject): def __init__(self): QtCore.QObject.__init__(self) self.my_id = QtCore.QString("I'm first") @QtCore.pyqtProperty(QtCore.QString) #(1) def some_id(self): return self.my_id so = Someone() 


First, consider the prop: function itself: similar to the previous function, access to an object is opened, but this time it is the class Someone. Note that it necessarily inherits QObject properties, otherwise QML will not accept this class. Now let's move on to the some_id function, which, unlike the previously considered form_move, returns a value. The line with the number (1) describes the type of this value and at the same time access to this function from QML is open. Again, the type value is QString instead of str.
Now the onClicked block in the QML file works, by clicking on the rectangle its text changes.

Conclusion


In my opinion, you should mainly use access from PyQt to QML, since this does not clutter the QML code as in the second case. And this method is much simpler and requires less code, which improves the readability of the program.
Python code
QML code

UPD: Sorry for missing indents. I don’t know that the code does not recognize them, but didn’t pay attention, but has already corrected it.

Source: https://habr.com/ru/post/113055/


All Articles