QQmlApplicationEngine list index out of range issue - qt

I am trying to run the last 2 examples from this website and end up with
root = engine.rootObjects()[0] # type: QObject IndexError: list index out of range error for both examples (Signal connection for root layout) (Signal connection of other qml file)
If it helps, I put all the example files onto the same directory level and changed the import statements to Pyside6 (from PySide6.QtCore...) instead of PySide2. I also added my version of the code if it makes it easier to see:
main.py:
import sys
from PySide6.QtCore import *
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtWidgets import QApplication
def say(s):
print(s)
if __name__ == '__main__':
app = QApplication()
engine = QQmlApplicationEngine()
engine.load(QUrl.fromLocalFile('main.qml'))
# Get the root object.
root = engine.rootObjects()[0] # type: QObject
# Find the target object. Since our target object is Window, which is the root object. So use it directly.
target_view = root
# Bind signal.
target_view.say.connect(say) # The former one is the signal of qml, and the latter one is from Python
# say() method.
sys.exit(app.exec())
main.qml:
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Window 2.12
Window {
visible: true
width: 600; height: 400
signal say(string s)
Button {
text: "hello"
onClicked: say(text)
}
}

Possible causes of the error are:
The path of the .qml is incorrect so the .qml is not loaded.
The loading of the .qml is not synchronous as you are assuming.
The .qml has some error (syntax for example)
Considering the above, the solution is:
import os
import sys
from pathlib import Path
from PySide6.QtCore import QCoreApplication, Qt, QUrl
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtWidgets import QApplication
CURRENT_DIRECTORY = Path(__file__).resolve().parent
def say(s):
print(s)
if __name__ == "__main__":
app = QApplication()
engine = QQmlApplicationEngine()
filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
url = QUrl.fromLocalFile(filename)
def handle_object_created(obj, obj_url):
if obj is None and url == obj_url:
QCoreApplication.exit(-1)
else:
root = engine.rootObjects()[0]
target_view = root
target_view.say.connect(say)
engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
engine.load(url)
sys.exit(app.exec())
But anyway I don't prefer to use rootObjects but instead export a QObject:
import os
import sys
from pathlib import Path
from PySide6.QtCore import QCoreApplication, QObject, Qt, QUrl, Signal
from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtWidgets import QApplication
CURRENT_DIRECTORY = Path(__file__).resolve().parent
class Helper(QObject):
say = Signal(str)
def say(s):
print(s)
if __name__ == "__main__":
app = QApplication()
engine = QQmlApplicationEngine()
helper = Helper()
helper.say.connect(say)
engine.rootContext().setContextProperty("helper", helper)
filename = os.fspath(CURRENT_DIRECTORY / "main.qml")
url = QUrl.fromLocalFile(filename)
def handle_object_created(obj, obj_url):
if obj is None and url == obj_url:
QCoreApplication.exit(-1)
engine.objectCreated.connect(handle_object_created, Qt.QueuedConnection)
engine.load(url)
sys.exit(app.exec())
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Window 2.12
Window {
visible: true
width: 600
height: 400
Button {
text: "hello"
onClicked: helper.say(text)
}
}

Related

How to create a "creatable" qml singleton?

I have a use case where I need to instantiate a singleton in qml (for property bindings).
i.e:
File: main.qml
Item{
MainQuery{id: mainquery
graphql: `query MainQuery{...}`
}
Text{
text: mainquery.data
}
}
This other file should use the query defined in main.qml
File: otherfile.qml
Item{
import MyLib 1.0 as Gql
Text{
text: Gql.MainQuery.data
}
}
However if you use QML_SINGLETON you can't initialize it in QML
and in order to bind to the graphql property above you would have to use
Connection.
Is there a better solution?
Note:
The object should be a singleton in CPP / Python side as well.
currently it is implemented in python like this:
class QSingleton(type(QObject)): # type: ignore
def __init__(cls, name, bases, dict):
super().__init__(name, bases, dict)
cls.instance = None
def __call__(cls, *args, **kw):
if cls.instance is None:
cls.instance = super().__call__(*args, **kw)
return cls.instance
class BaseQuery(QObjcet, QSingleton):
...
#QmlElement
class MainQuery(BaseQuery):
...
And gives this nice error when the qml engine tries to initiate the type:
QEventLoop: Cannot be used without QApplication
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)
(I do have an eventloop running)
Whether the underlying Python object is or is not a singleton it doesn't matter. We can always declare a singleton wrapper in QML by defining a qmldir and using pragma Singleton in our source file. To demonstrate this approach, I wrap TextMetrics in my singleton object and expose TextMetrics properties as properties of my singleton so that I can use property binding with singleton syntax:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
Frame {
anchors.centerIn: parent
ColumnLayout {
TextField {
text: "Hello"
onTextChanged: MyWrapper.tmText = text
}
Text {
text: MyWrapper.tmWidth
}
}
}
}
// qmldir
module ExampleWrapper
singleton MyWrapper 1.0 MyWrapper.qml
// MyWrapper.qml
pragma Singleton
import QtQuick
import QtQuick.Controls
Item {
property alias tmText: tm.text
property alias tmWidth: tm.width
TextMetrics {
id: tm
text: "Hello"
}
}
You can Try it Online!
References:
https://doc.qt.io/qt-6/qtqml-modules-qmldir.html

Connecting and sending a message in bluetooth using python and PyQt5

I have been trying to connect to a Bluetooth device (regular, not low energy - an HC-06) using PyQt5 but with no success. I can connect to the same device using standard python calls. I am running Python 3.7 on MacOS. When I run the below code, I get the error: 'unknown error', I'd be happy to know what I am doing wrong.
import sys
import bluetooth
import os
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt
from PyQt5 import QtBluetooth
class bluetoothTest(QWidget):
def __init__(self, parent = None):
super(bluetoothTest, self).__init__(parent)
self.connectToRobot()
self.app = QApplication(sys.argv)
self.win = QWidget()
self.win.show()
sys.exit(self.app.exec_())
def connectToRobot(self):
self.sock = QtBluetooth.QBluetoothSocket(bluetooth.RFCOMM)
self.sock.connected.connect(self.connectedToBluetooth)
self.sock.readyRead.connect(self.receivedBluetoothMessage)
self.sock.disconnected.connect(self.disconnectedFromBluetooth)
self.sock.error.connect(self.socketError)
port = 1
self.sock.connectToService(QtBluetooth.QBluetoothAddress("98:D3:C1:FD:2C:46"),port)
def socketError(self,error):
print(self.sock.errorString())
def connectedToBluetooth(self):
self.sock.write('A'.encode())
def disconnectedFromBluetooth(self):
self.print('Disconnected from bluetooth')
def receivedBluetoothMessage(self):
while sock.canReadLine():
line = sock.readLine()
print(line)
def main():
# deal with a bluetooth bug on mac
if sys.platform == 'darwin':
os.environ['QT_EVENT_DISPATCHER_CORE_FOUNDATION'] = '1'
app = QApplication(sys.argv)
ex = bluetoothTest()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The problem with my code was the mixing of "regular" Bluetooth and Qt Bluetooth functions, in particular this line:
self.sock = QtBluetooth.QBluetoothSocket(bluetooth.RFCOMM)
which should have been
self.sock = QtBluetooth.QBluetoothSocket(QtBluetooth.QBluetoothServiceInfo.RfcommProtocol)
Here is the complete working code example:
import sys
import os
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtCore import Qt
from PyQt5 import QtBluetooth
class bluetoothTest(QWidget):
def __init__(self, parent = None):
super(bluetoothTest, self).__init__(parent)
self.connectToRobot()
self.win = QWidget()
self.win.show()
def connectToRobot(self):
self.sock = QtBluetooth.QBluetoothSocket(QtBluetooth.QBluetoothServiceInfo.RfcommProtocol)
self.sock.connected.connect(self.connectedToBluetooth)
self.sock.readyRead.connect(self.receivedBluetoothMessage)
self.sock.disconnected.connect(self.disconnectedFromBluetooth)
self.sock.error.connect(self.socketError)
port = 1
self.sock.connectToService(QtBluetooth.QBluetoothAddress("98:D3:C1:FD:2C:46"),port)
def socketError(self,error):
print(self.sock.errorString())
def connectedToBluetooth(self):
self.sock.write('A'.encode())
def disconnectedFromBluetooth(self):
self.print('Disconnected from bluetooth')
def receivedBluetoothMessage(self):
while self.sock.canReadLine():
line = self.sock.readLine()
print(str(line, "utf-8"))
def main():
# deal with a bluetooth bug on mac
if sys.platform == 'darwin':
os.environ['QT_EVENT_DISPATCHER_CORE_FOUNDATION'] = '1'
app = QApplication(sys.argv)
ex = bluetoothTest()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

ListView doesn't update when its model data changes

I have a QList<QObject*> that is read from within a ListView module into a model. At first the QList is an empty object. I want the indexBackup to be updated when I assign a new list to the QList object.
import QtQuick 2.5
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.2
import QtQuick.Controls.Styles 1.0
ListView {
id: layersListView
model: settingsList //QList<QObject*> object
int indexBackup: 0
onModelChanged: {
if(indexBackup && indexBackup < model.length)
{
indexBackup = model.length
}
else
{
indexBackup = - 1
}
}
}
For everybody with similar problem, the answer can be found here.

declaring string in .qml files

I am trying to make a varaibles file in qml which declares several strings to be used by other qml files. for example: this would be Variables.qml
import QtQuick 2.0
Item {
property var mystring: qsTr("hello world")
}
and I want to reference it in another file tab1.ui.qml
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.3
import "."
Item {
Rectangle {
id: rectangle
color: "#3e1edd"
anchors.fill: parent
Text {
id: text6
anchors.fill: parent
font.pixelSize: 12
text: Variables.mystring
visible: true
}
}
}
this gives an error: Unable to assign [undefined] to QString
please tell me if there is a better way to manage variables. I want them to be global so they can be used in multiple frames. Thanks
You must use a singleton, for it you must create a folder that contains the .qml with "pragma Singleton" at the top and a qmldir that indicate the files:
Global
├── qmldir
└── Variables.qml
qmldir
singleton Variables 1.0 Variables.qml
Variables.qml
pragma Singleton
import QtQuick 2.0
QtObject {
property var mystring1: qsTr("hello world1")
property var mystring2: qsTr("hello world2")
}
Then you must import and use it in the following way:
// others imports
import "Global"
// ...
Text{
// ...
text: Variables.mystring1
}
In this link you will find an example.

PYQT4 Python 3.4 Creating a form with lable

I am trying to create a window with a label and i get this error
File "ui.py", line 16, in mainWindow = MyForm() File
"ui.py", line 9, in init lbl = QtGui.QLable('Welcome Brats!',
self) ttributeError: 'module' object has no attribute 'QLable'
import sys
from PyQt4 import QtGui
class MyForm(QtGui.QWidget):
def __init__(self):
super(MyForm, self).__init__()
lbl = QtGui.QLable('Welcome Brats!', self)
self.setGeometry(300,300,250,150)
self.setWindowTitle(('Port Scaner'))
self.show()
app = QtGui.QApplication(sys.argv)
mainWindow = MyForm()
status = app.exec_()
sys.exit(status)
Pyqt4 python3.4

Resources