How do I update the progress bar on my Pyside2 GUI? - python

I've been trying to update my progress bar on my GUI but I can't figure out how to do it... The code I have provided shows the progress updating - I just need that value to update the progress bar on my GUI too! It's a QML file from Qt Creator.
main.py
import os
import sys
import time
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, qmlRegisterType
from PySide2.QtCore import QObject, Slot, Signal, QTimer, QUrl, QThread
from pathlib import Path
from multiprocessing.pool import ThreadPool
class MainWindow(QObject):
def __init__(self):
QObject.__init__(self)
#Slot()
def thread_progress(self):
print("Worker")
self.worker = Worker()
self.thread = QThread()
self.worker.moveToThread(self.thread)
self.thread.start()
self.worker.run()
class Worker(QObject):
progress_value = Signal(float)
#Slot()
def run(self):
self.progress = 0
self.total = 100
for i in range(0, self.total):
self.update_progress()
def update_progress(self):
print(f"{self.progress} / {self.total}")
self.progress += 1
self.progress_value.emit(self.progress)
time.sleep(1)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
# Get Context
main = MainWindow()
engine.rootContext().setContextProperty("backend", main)
# Set App Extra Info
app.setOrganizationName("zardoss")
app.setOrganizationDomain("N/A")
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
The main.py file connects with the main.qml file. The GUI has a button, progress bar and a text input element. I am only concerned with the progress bar filling up after the generate button has been pressed.
You can see from the console after pressing the generate button that the values from 1-100 are being filled up but it doesn't fill the progress bar as I am unsure how.
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import QtQuick.Shapes 1.15
Window {
id: mainWindow
width: 750
height: 500
visible: true
color: "#00000000"
// Remove title bar
flags: Qt.Window | Qt.FramelessWindowHint
// Properties
property int windowStatus: 0
property int windowMargin: 10
title: qsTr("Progress Bar")
Rectangle {
id: bg
color: "#2c313c"
border.color: "#f12c313c"
border.width: 1
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: windowMargin
anchors.leftMargin: windowMargin
anchors.bottomMargin: windowMargin
anchors.topMargin: windowMargin
z:1
Rectangle {
id: appContainer
height: 480
color: "#00000000"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.rightMargin: 1
anchors.leftMargin: 1
anchors.bottomMargin: 1
anchors.topMargin: 1
Rectangle {
id: content
color: "#00000000"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: topBar.bottom
anchors.bottom: parent.bottom
anchors.topMargin: 0
Button {
id: btnGenerate
x: 265
y: 44
width: 200
height: 50
text: "Generate"
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.horizontalCenterOffset: 0
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenterOffset: 165
// colorPressed: "#1e5425"
// colorMouseOver: "#42b852"
font.pointSize: 14
display: AbstractButton.TextBesideIcon
font.bold: false
// colorDefault: "#328a3f"
anchors.rightMargin: 250
onPressed: {
// backend.generate()
backend.thread_progress()
}
}
ProgressBar{
id: progressBar
x: 239
y: 64
visible: true
width: 661
height: 250
// text: "%"
anchors.verticalCenter: parent.verticalCenter
value: 0
//bgColor: "#00000/*000"
//dropShadowColor: "#20000000"
//samples: 16
anchors.verticalCenterOffset: -15
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
DropShadow{
anchors.fill: bg
horizontalOffset: 0
verticalOffset: 0
radius: 10
samples: 16
color: "#80000000"
source:bg
z: 0
}
Connections{
target: backend
function onLinkValid(valid) {
if(valid === true) {
textField.textColor = "#00FF00"
} else {
textField.textColor = "#FF00FF"
}
}
}
}

The problem in your case is that the signal that has the progress value is not connected to any element of the GUI.
In this case, a signal must be created in the class exposed to QML that connects with the ProgressBar, and this signal must receive information from the signal from the Worker. On the other hand, you should not invoke the "run()" method directly since it will be executed in the thread from which it was invoked, which in your case is the main thread so it will block the GUI, instead you must invoke it indirectly with a signal or using the QTimer.
import os
import sys
import time
from pathlib import Path
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal, QTimer, QUrl, QThread
class Backend(QObject):
progress_changed = Signal(float, name="progressChanged")
def __init__(self, parent=None):
super().__init__(parent)
self.worker = Worker()
self.worker.progress_changed.connect(self.progress_changed)
self.worker_thread = QThread()
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
#Slot()
def start_worker(self):
QTimer.singleShot(0, self.worker.run)
class Worker(QObject):
progress_changed = Signal(float)
#Slot()
def run(self):
self.progress = 0
self.total = 100
for i in range(0, self.total):
self.update_progress()
def update_progress(self):
print(f"{self.progress} / {self.total}")
self.progress += 1
self.progress_changed.emit(self.progress)
time.sleep(1)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
engine.load(os.fspath(Path(__file__).resolve().parent / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
ret = app.exec_()
# backend.worker_thread.quit()
# backend.worker_thread.wait()
sys.exit(ret)
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
width: 750
height: 500
visible: true
color: "#00000000"
title: qsTr("Progress Bar")
Row {
spacing: 10
anchors.centerIn: parent
Button {
id: btnGenerate
text: "start"
onClicked: backend.start_worker()
}
ProgressBar {
id: progressBar
from: 0
to: 100.0
}
}
Connections {
target: backend
function onProgressChanged(progress) {
progressBar.value = progress;
}
}
}

Related

Qml, Reference error <signalName> is not defined when signal defined

I have a very simple qml+python app to play and test signal/slot communication.
All works fine so far, but when I run the app, a ReferenceError is reported on the QML side.
However, all works fine, it is so simple code:
QML:
import QtQuick 2.0
import QtQuick.Window 2.0
Window {
width: 1000
height: 480
visible: true
title: qsTr("Hello World")
Connections {
target: signalEmitter
ignoreUnknownSignals : true
function onSignal() {
console.log("HELLO QML")
}
}
Rectangle{
height: 100
width: 100
color: "green"
MouseArea {
anchors.fill: parent
onClicked: {
signalEmitter.sayHello()
}
}
}
Rectangle{
anchors.fill: parent
color: "transparent"
border.color: "black"
}
}
Python:
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
import sys
class PythonSignalEmitter(QObject):
signal = Signal(str)
#Slot()
def sayHello(self):
print("HELLO PYTHON")
self.signal.emit("HELLO")
if __name__ == '__main__':
app = QGuiApplication([])
engine = QQmlApplicationEngine()
engine.load("main.qml")
signal_emitter = PythonSignalEmitter()
engine.rootContext().setContextProperty("signalEmitter", signal_emitter)
sys.exit(app.exec())
Why do I keep getting the error:
ReferenceError: signalEmitter is not defined
on line 12 in qml file. (app runs and signal/slot works as expected)
You should add the context properties before loading any qml:
if __name__ == '__main__':
app = QGuiApplication([])
engine = QQmlApplicationEngine()
signal_emitter = PythonSignalEmitter()
engine.rootContext().setContextProperty("signalEmitter", signal_emitter)
engine.load("main.qml") # new place
sys.exit(app.exec())

How show a glowing label in QMainWindow

I'm trying to figure out how to use RectangularGlow to create a glowing label with PyQt5, but I don't know about QML, and I can't figure out how to do. Here is what I have so far:
example.qml:
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.12
Label {
text: "Identifiant"
width: 400
height: 200
Rectangle {
id: background
anchors.fill: parent
color: "black"
}
RectangularGlow {
id: effect
anchors.fill: rect
glowRadius: 10
spread: 0.2
color: "white"
cornerRadius: rect.radius + glowRadius
}
Rectangle {
id: rect
color: "black"
anchors.centerIn: parent
width: Math.round(parent.width / 1.5)
height: Math.round(parent.height / 2)
radius: 25
}
}
And the python code that uses it:
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtWidgets import *
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
engine.load('example.qml')
label = engine.rootObjects()[0]
win = QMainWindow()
win.setCentralWidget(label)
win.show()
sys.exit(app.exec_())
But I get this error:
Traceback (most recent call last):
File "test2.py", line 16, in <module>
win.setCentralWidget(label)
TypeError: setCentralWidget(self, QWidget): argument 1 has unexpected type 'QObject'
If you want to use qml items in Qt Widgets then you must use a QQuickWidget (or QQuickView + QWidget::createWindowContainer()):
import os.path
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtQuickWidgets import QQuickWidget
CURRENT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)))
if __name__ == "__main__":
app = QApplication(sys.argv)
filename = os.path.join(CURRENT_DIR, "example.qml")
qml_widget = QQuickWidget()
qml_widget.setResizeMode(QQuickWidget.SizeRootObjectToView)
qml_widget.setSource(QUrl.fromLocalFile(filename))
win = QMainWindow()
win.setCentralWidget(qml_widget)
win.show()
sys.exit(app.exec_())
you can have glow effect with :
import QtGraphicalEffects 1.12
also this is a label with glow effect :
Rectangle {
width: 50
clip: true
height: 20
color: "#592e2e2e"
layer.enabled: true
layer.effect: Glow {
samples: 14
color: "#592e2e2e"
transparentBorder: true
}
Text {
clip: true
anchors.fill: parent
wrapMode: Text.WrapAnywhere
horizontalAlignment: Text.AlignHCenter
}
}

How to select row in listview

I search on the web but I don't find how select a row in a listview create in qml file with python. I test but each time I have error.
I just start qml and perhaps the base is not good. So my question, with my code, it's possible to acceed to my ListView ? If yes how can I select, in start application, the second item (for example) ?
When I do that :
print(win.findChild(QObject, "listview22").setCurrentIndex(2)))
I have this message
AttributeError: 'QQuickItem' object has no attribute 'setCurrentIndex'
But listview22 is a listview not an item.
Thank you for your help.
PS : I just start qml so if my script are no good tell me and then I can learn good programmation
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ApplicationWindow {
title: qsTr("WoodMan Training")
width: 1000
height: 700
visible: true
GridLayout {
id: grid
anchors.fill: parent
columns: 3
anchors.margins: 0
columnSpacing: 0
ColumnLayout {
Layout.columnSpan: 1
Layout.row: 2
Layout.column: 0
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
Layout.maximumWidth: 250
// ---
RowLayout{
Main_ListView{objectName: "lstGroupe"; id:lstGroupe; pyModel: ModelGroupe; pyClass: ModelGroupe}
}
Item {Layout.fillWidth: true; Layout.fillHeight: true}
}
}
}
Main_ListView.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
Rectangle {
property var pyClass: 0
property var pyModel: 0
border.color: "red"
Layout.fillWidth: true
height:150
ListView {
anchors.topMargin: 10
anchors.bottomMargin: 10
anchors.leftMargin: 10
anchors.fill: parent
id: listview22
objectName: "listview22"
model: pyModel
clip: true // --- Empeche la surbrillance de sortir du cadre
delegate: Component {
Item {
width: 200
height: 20
property int item_id: iid
Row {
Text {
anchors.verticalCenter: parent.verticalCenter
width: 60
text: " " + libelle
}
}
MouseArea {
anchors.fill: parent
onClicked: {
listview22.currentIndex = index
pyClass.onClickItem(item_id)
}
}
}
}
highlight: Rectangle {color: '#CDCDCD'}
focus: true
}
Button {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 5
anchors.bottomMargin: 5
width: 28
height: 25
iconSource: "ico/math-add-icon.png"
onClicked: {
//console.log("qml adding")
pyClass.onClickInsert(22, "aluis")
}
}
}
Main.py
from PyQt5.QtCore import QAbstractListModel, Qt, pyqtSignal, pyqtSlot, QModelIndex
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQuick import QQuickView
from PyQt5.QtGui import *#QGuiApplication, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtQml import *#QQmlApplicationEngine, QQmlComponent
from PyQt5.QtCore import *#QUrl, Qt, QCoreApplication, QAbstractListModel, QModelIndex, QTimer, qsrand, qrand, QTime, QObject
from PyQt5.QtQuick import *#QQuickItem
class gGroupe(QAbstractListModel):
iid = Qt.UserRole + 1
libelle = Qt.UserRole + 2
data_change = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.liste = []
self.insert_list()
self.liste2 = {}
self.createIndex(0,0,125)
self.sort(1, order = Qt.DescendingOrder)
# ------------------------------------------------------------------
def roleNames(self):
return {
gGroupe.iid: b'iid',
gGroupe.libelle: b'libelle',
}
def data(self, index, role=Qt.DisplayRole):
row = index.row()
if role == gGroupe.iid:
return self.liste[row]["iid"]
if role == gGroupe.libelle:
return self.liste[row]["libelle"]
def rowCount(self, parent=QModelIndex()):
return len(self.liste)
# ------------------------------------------------------------------
def insert_list(self):
self.liste = [
{'iid': 10, 'libelle': 'groupe10'},
{'iid': 11, 'libelle': 'groupe11'},
{'iid': 12, 'libelle': 'groupe12'},
]
def delete(self, row):
self.beginRemoveRows(QModelIndex(), row, row)
del self.liste[row]
self.endRemoveRows()
def delete_all(self):
self.beginRemoveRows(QModelIndex(), 0, len(self.liste))
del self.liste[row]
self.endRemoveRows()
def select(self):
pass
#pyqtSlot(int, str)
def onClickInsert(self, iid=10, libelle='toto'):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.liste.append({'iid': iid, 'libelle': libelle})
self.endInsertRows()
return self.index(len(self.liste)-1)
#pyqtSlot(int)
def onClickItem(self, iid):
print(iid)
"""
#pyqtSlot(int, str, int)
def editPerson(self, row, name, age):
ix = self.index(row, 0)
self.persons[row] = {'iid': 22, 'name': name, 'age': age}
self.dataChanged.emit(ix, ix, self.roleNames())
"""
class MainWindow(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.g_groupe = gGroupe()
engine.rootContext().setContextProperty('ModelGroupe', self.g_groupe)
engine.load('main.qml')
win = engine.rootObjects()[0]
#win.findChild(QObject, "txtLibelle").setProperty("text", 'toto')
print(win.findChild(QObject, "listview22").setCurrentIndex(2))
a = win.findChild(QObject, "PersonModel")#.removeRows( 0, model.rowCount() )
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
# Création d'un objet QQmlContext pour communiquer avec le code QML
#ctx = engine.rootContext()
py_mainapp = MainWindow()
sys.exit(app.exec())
You have the following errors:
You should not export QML objects to python since it can bring you many errors like the one you get and even more critical errors, that is recommended in C++.
When trying to obtain the ListView using findChild (QObject, "listview22") in C++, only one QObject would be obtained but python tries to deduce the type and for that it has as a limit the public API of Qt, in your ListView it is not a QListView, and its type does not exist in Python or C++ since it is part of the private API, so a nearby class is QQuickItem since ListView inherits from that class.
What is recommended is to create a QObject and add qproperties that map the logic and export to QML, so if you modify the QObject in python it will modify it in QML.
main.py
from PyQt5.QtCore import (
pyqtProperty,
pyqtSignal,
pyqtSlot,
QAbstractListModel,
QModelIndex,
QObject,
Qt,
QTimer,
)
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
class gGroupe(QAbstractListModel):
iid = Qt.UserRole + 1
libelle = Qt.UserRole + 2
data_change = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.liste = []
self.insert_list()
self.sort(0, order=Qt.DescendingOrder)
def roleNames(self):
return {gGroupe.iid: b"iid", gGroupe.libelle: b"libelle"}
def data(self, index, role=Qt.DisplayRole):
row = index.row()
if role == gGroupe.iid:
return self.liste[row]["iid"]
if role == gGroupe.libelle:
return self.liste[row]["libelle"]
def rowCount(self, parent=QModelIndex()):
return len(self.liste)
# ------------------------------------------------------------------
def insert_list(self):
self.beginResetModel()
self.liste = [
{"iid": 10, "libelle": "groupe10"},
{"iid": 11, "libelle": "groupe11"},
{"iid": 12, "libelle": "groupe12"},
]
self.endResetModel()
def delete(self, row):
self.beginRemoveRows(QModelIndex(), row, row)
del self.liste[row]
self.endRemoveRows()
def delete_all(self):
self.beginRemoveRows(QModelIndex(), 0, len(self.liste))
del self.liste[row]
self.endRemoveRows()
def select(self):
pass
#pyqtSlot(int, str)
def insert(self, iid=10, libelle="toto"):
self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
self.liste.append({"iid": iid, "libelle": libelle})
self.endInsertRows()
#pyqtSlot(int)
def onClickItem(self, iid):
print(iid)
class MainWindow(QObject):
currentIndexChanged = pyqtSignal(int)
def __init__(self, parent=None):
super().__init__(parent)
self._g_groupe = gGroupe(self)
self._current_index = 0
#pyqtProperty(QObject, constant=True)
def g_groups(self):
return self._g_groupe
#pyqtProperty(int, notify=currentIndexChanged)
def currentIndex(self):
return self._current_index
#currentIndex.setter
def currentIndex(self, index):
if self._current_index == index:
return
self._current_index = index
self.currentIndexChanged.emit(index)
if __name__ == "__main__":
import sys
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
main_window = MainWindow()
engine.rootContext().setContextProperty("main_window", main_window)
engine.load("main.qml")
if not engine.rootObjects():
sys.exit(app.exec_())
main_window.currentIndex = 2
sys.exit(app.exec())
main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
ApplicationWindow {
title: qsTr("WoodMan Training")
width: 1000
height: 700
visible: true
Connections{
target: main_window
onCurrentIndexChanged: lstGroupe.currentIndex = main_window.currentIndex
}
GridLayout {
id: grid
anchors.fill: parent
columns: 3
anchors.margins: 0
columnSpacing: 0
ColumnLayout {
Layout.columnSpan: 1
Layout.row: 2
Layout.column: 0
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 10
Layout.maximumWidth: 250
// ---
RowLayout{
Main_ListView{
id:lstGroupe;
model: main_window.g_groups
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
}
Main_ListView.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
Rectangle {
id: root
property alias model: listview22.model
property alias currentIndex: listview22.currentIndex
border.color: "red"
Layout.fillWidth: true
height:150
ListView {
id: listview22
model: root.model
anchors.topMargin: 10
anchors.bottomMargin: 10
anchors.leftMargin: 10
anchors.fill: parent
clip: true
delegate: Component {
Item {
width: 200
height: 20
property int item_id: iid
Row {
Text {
anchors.verticalCenter: parent.verticalCenter
width: 60
text: " " + libelle
}
}
MouseArea {
anchors.fill: parent
onClicked: {
listview22.currentIndex = index
listview22.model.onClickItem(item_id)
}
}
}
}
highlight: Rectangle {color: '#CDCDCD'}
focus: true
}
Button {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: 5
anchors.bottomMargin: 5
width: 28
height: 25
iconSource: "ico/math-add-icon.png"
onClicked: {
listview22.model.insert(22, "aluis")
}
}
}

QML: Setting "source" property for image causes it to disappear

What I'm trying to do is update the source of an image with QML in PyQt5. When I use element.setProperty("source", "./testImage.png") to change the image I get the following error message.
QML Image: Protocol "" is unknown
Any idea on how to fix this?
I've looked at other ways to interact with QML elements and if possible I'd like to stick with changing the image through Python code rather then through just QML.
main.py
from PyQt5.QtWidgets import *
from PyQt5.QtQml import *
from PyQt5.QtCore import *
from PyQt5.QtQuick import *
from PyQt5 import *
import sys
import resource_rc
class MainWindow(QQmlApplicationEngine):
def __init__(self):
super().__init__()
self.load("main.qml")
self.rootContext().setContextProperty("MainWindow", self)
self.window = self.rootObjects()[0]
self.cardLeft = self.window.findChild(QObject, "cardLeft")
#pyqtSlot()
def changeImage(self):
self.cardLeft.setProperty("source", "./images/3_of_clubs.png")
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
main.qml
import QtQml 2.11
import QtQuick 2.3
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.1
import QtQuick.Layouts 1.11
ApplicationWindow{
id: screen
width: 720
height: 490
visible: true
Material.theme: Material.Dark
Item {
id: cards
width: parent.width
height: parent.height - toolBar.height
anchors {
top: parent.top;
bottom: toolBar.top;
bottomMargin: 10
}
RowLayout {
anchors.fill: parent
spacing: 20
Image {
objectName: "cardLeft"
id: cardLeft
Layout.fillWidth: true
Layout.maximumHeight: 250
Layout.minimumHeight: parent.height
Layout.margins: 20
source: "./testImage.png"
fillMode: Image.PreserveAspectFit
}
}
}
Button {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumHeight: 100
Layout.maximumWidth: 300
Layout.margins: 20
text: qsTr("Change Image")
highlighted: true
Material.accent: Material.color(Material.LightGreen)
onClicked: MainWindow.changeImage()
}
}
You have to pass a QUrl for it you must use QUrl::fromLocalFile():
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
# import resource_rc
dir_path = os.path.dirname(os.path.realpath(__file__))
class MainWindow(QtQml.QQmlApplicationEngine):
def __init__(self):
super().__init__()
self.load(QtCore.QUrl.fromLocalFile(os.path.join(dir_path, "main.qml")))
self.rootContext().setContextProperty("MainWindow", self)
if self.rootObjects():
self.window = self.rootObjects()[0]
self.cardLeft = self.window.findChild(QtCore.QObject, "cardLeft")
#QtCore.pyqtSlot()
def changeImage(self):
if self.cardLeft:
url = QtCore.QUrl.fromLocalFile(os.path.join(dir_path, "images/3_of_clubs.png"))
self.cardLeft.setProperty("source", url)
if __name__ == '__main__':
app = QtGui.QGuiApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())

Can't pass a QAbstractListModel to QML

I'm following this PySide tutorial as close as possible using PyQt5. When I run my code, I get this error: ReferenceError: pythonListModel is not defined, and the list shows up black with no items.
This is my code
def main():
platform = Platform("Windows")
platform_wrp = qml_platforms.PlatformsWrapper(platform)
platform_model = qml_platforms.PlatformsListModel([platform_wrp])
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine(QUrl("main.qml"))
context = engine.rootContext()
context.setContextProperty('pythonListModel', platform_model)
window = engine.rootObjects()[0]
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
my model and wrapper
class PlatformsWrapper(QObject):
def __init__(self, platform):
QObject.__init__(self)
self.platform = platform
def full_name(self):
return str(self.platform.full_name)
changed = pyqtSignal()
full_name = pyqtProperty("QString", _full_name, notify=changed)
class PlatformsListModel(QAbstractListModel):
def __init__(self, platforms):
QAbstractListModel.__init__(self)
self.platforms = platforms
def rowCount(self, parent=QModelIndex()):
return len(self.platforms)
def data(self, index):
if index.isValid():
return self.platforms[index.row()]
return None
and my QML
import QtQuick 2.1
import QtQuick.Controls 1.1
ApplicationWindow{
ListView {
id: pythonList
width: 400
height: 200
model: pythonListModel
delegate: Component {
Rectangle {
width: pythonList.width
height: 40
color: ((index % 2 == 0)?"#222":"#111")
Text {
id: title
elide: Text.ElideRight
text: model.platform.full_name
color: "white"
font.bold: true
anchors.leftMargin: 10
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
}
MouseArea {
anchors.fill: parent
}
}
}
}
}
Why can't Qt find my contextProperty?
The problem is that "main.qml" is loaded before you set context property. Try load file after you setup your context:
def main():
platform = Platform("Windows")
platform_wrp = qml_platforms.PlatformsWrapper(platform)
platform_model = qml_platforms.PlatformsListModel([platform_wrp])
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
context = engine.rootContext()
context.setContextProperty('pythonListModel', platform_model)
engine.load( QUrl("main.qml") ) #load after context setup
window = engine.rootObjects()[0]
window.show()
sys.exit(app.exec_())

Categories

Resources