PySide Signal argument can't be retrieved from QML
According to this post, PySide2 (version > 5.12.5) supports signal with named parameter like in PyQt5. So i tried here in PySide2 (5.14.2) and got error like
file:///E:/QML/projects/main.qml:72:5: Cannot assign to non-existent property "onSpitInput"
Tell me what's wrong here.
*app.py
import os
import sys
from PySide2 import QtCore, QtGui, QtWidgets, QtQml
class controller(QtCore.QObject):
spitInput = QtCore.Signal(str, arguments=['userinput'])
def __init__(self):
QtCore.QObject.__init__(self)
#QtCore.Slot(int, result=list)
def getUserInput(self, first):
self.spitInput.emit(str(first) + 'is the value given by user')
controller = controller()
app = QtWidgets.QApplication(sys.argv)
current_dir = os.path.dirname(os.path.realpath(__file__))
engine = QtQml.QQmlApplicationEngine()
engine.addImportPath(current_dir)
engine.rootContext().setContextProperty("controller", controller)
filename = os.path.join(current_dir, "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
engine.quit.connect(app.quit)
sys.exit(app.exec_())
main.qml
import QtQuick 2.13
import QtQuick.Controls 2.13
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: bufferRectId
width: 640
height: 480
anchors.fill: parent
TextField{
id:firstTextInputFieldId
font.pointSize: 16
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
}
Button{
id:calcButtonId
width: 60
height: 30
text: "Click Me"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 60
onClicked: {
controller.getUserInput(firstTextInputFieldId.text)
}
}
}
onSpitInput: console.log(userinput)
}
When you use the following code:
ApplicationWindow {
id: root
// ...
onSpitInput: console.log(userinput)
}
You are stating that onSpitInput belongs to "root" which is clearly false since it belongs to "controller" and therefore fails.
In this case you should use Connections:
import QtQuick 2.13
import QtQuick.Controls 2.13
ApplicationWindow {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
id: bufferRectId
anchors.fill: parent
TextField{
id:firstTextInputFieldId
font.pointSize: 16
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.horizontalCenter: parent.horizontalCenter
}
Button{
id:calcButtonId
width: 60
height: 30
text: "Click Me"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 60
onClicked: controller.getUserInput(firstTextInputFieldId.text)
}
}
Connections{
target: controller
onSpitInput: console.log(userinput)
}
}
On the other hand there is another error: The getUserInput method receives a string and does not return anything, but according to your code it must receive an integer and return a list. The correct is:
import os
import sys
from PySide2 import QtCore, QtGui, QtWidgets, QtQml
class controller(QtCore.QObject):
spitInput = QtCore.Signal(str, arguments=["userinput"])
#QtCore.Slot(str)
def getUserInput(self, first):
self.spitInput.emit("{} is the value given by user".format(first))
if __name__ == "__main__":
controller = controller()
app = QtWidgets.QApplication(sys.argv)
current_dir = os.path.dirname(os.path.realpath(__file__))
engine = QtQml.QQmlApplicationEngine()
engine.addImportPath(current_dir)
engine.rootContext().setContextProperty("controller", controller)
filename = os.path.join(current_dir, "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
engine.quit.connect(app.quit)
sys.exit(app.exec_())
Related
i using ffmpeg in my qt-python app and i redirecting the STDOUT of subprocess to a textarea in qml file , the problem is when redirects without append works fine ,but when appending text into textarea it making gui freezing with high ram usage, how to append stream text without lag, please
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Flickable {
id: scrollView
x: 13
y: 38
width: 647
height: 168
contentWidth: text1.width; contentHeight: text1.height
visible: true
flickableDirection: Flickable.VerticalFlick
clip: true
ScrollBar.vertical: ScrollBar{}
ScrollBar.horizontal: ScrollBar{}
TextArea {
id: text1
opacity: 1
visible: true
color: "#000000"
font.pixelSize: 16
verticalAlignment: Text.AlignVCenter
font.preferShaping: false
font.kerning: false
font.styleName: "Courier New"
font.weight: Font.Medium
font.bold: true
readOnly: true
clip: true
font.family: "Courier New"
}
}
Connections {
target: con
function onSetTx(filer){
text1.append(filer)
}
}
}
main.py
import os
from pathlib import Path
import sys
from time import sleep
import subprocess
import re
from threading import Thread
from PySide2.QtCore import QObject, Slot, Signal
from PySide2.QtGui import QGuiApplication, QIcon
from PySide2.QtQml import QQmlApplicationEngine
class Bridge(QObject):
setTx = Signal(str)
def __init__(self):
QObject.__init__(self)
Thread(target=self.download).start()
def download(self):
k = subprocess.Popen( f'"{Path(__file__).parent / "ffmpeg"}" "Rest of arg here', shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, encoding='latin-1')
while k.poll() is None:
std = k.stdout.readline()
empty = re.compile('^$')
if empty.match(std):
pass
else:
self.setTx.emit(std)
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
bridge = Bridge()
context = engine.rootContext()
context.setContextProperty("con", bridge)
qmlFile = Path(__file__).parent / 'main.qml'
engine.load(os.fspath(qmlFile.resolve()))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
Is there a way for me to treat QML components as objects, and initialize them in Python? For instance, here's a simplified QML for a box:
I want to be able to replicate what a constructor method can do in Java. I want to be able to customize the text on each box through the Python script and also, at the same time, create multiple box instances that are separate from each other.
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: boxItem
width: 800
height: 118
Rectangle {
id: boxRect
height: 118
color: "#55f555"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Text {
id: textItem
width: 463
height: 43
color: "#000000"
text: qsTr("Header Text")
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: 38
verticalAlignment: Text.AlignVCenter
font.family: "Roboto"
textFormat: Text.AutoText
anchors.leftMargin: 20
anchors.topMargin: 20
}
}
}
This is my current Python script modified from Qt's template version:
import os
import sys
from pathlib import Path
import PySide6.QtQml
from PySide6.QtQuick import QQuickView
from PySide6.QtCore import Qt, QUrl
from PySide6.QtGui import QGuiApplication
if __name__ == '__main__':
#Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
#Load the QML file
qml_file = Path(__file__).parent / "Main.qml"
view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))
#Show the window
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
#execute and cleanup
app.exec()
del view
Quick clarification: I am working with custom built QML components, not trying to edit pre-existing ones made by QT.
Applying the concepts of another technology (programming language, library, framework) are often a bad approach to use some other technology. Each technology has its own methodology and good practices to implement any requirement.
In the case of QML the other languages such as C++, python, etc. are generally considered to implement business logic (something like a backend) and QML takes care of the view and the operation. In this case, it is recommended to create a QObject that provides other QObjects that create, modify, etc. the data and this can be reflected in the view. In this particular case, for the requirement, it is enough to use a model and a custom qml Item.
Box.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: boxItem
width: 800
height: 118
property alias text: textItem.text
Rectangle {
id: boxRect
height: 118
color: "#55f555"
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
Text {
id: textItem
width: 463
height: 43
color: "#000000"
anchors.left: parent.left
anchors.top: parent.top
font.pixelSize: 38
verticalAlignment: Text.AlignVCenter
font.family: "Roboto"
textFormat: Text.AutoText
anchors.leftMargin: 20
anchors.topMargin: 20
}
}
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
Item {
id: root
ScrollView
{
anchors.fill: parent
Column{
Repeater{
model: manager.model
Box{
text: model.display
}
}
}
}
}
main.py
import os
import sys
from pathlib import Path
from PySide6.QtCore import Property, QObject, Qt, QUrl
from PySide6.QtGui import QGuiApplication, QStandardItemModel, QStandardItem
from PySide6.QtQuick import QQuickView
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStandardItemModel()
#Property(QObject, constant=True)
def model(self):
return self._model
if __name__ == "__main__":
# Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
manager = Manager()
view.rootContext().setContextProperty("manager", manager)
qml_file = Path(__file__).parent / "main.qml"
view.setSource(QUrl.fromLocalFile(os.fspath(qml_file.resolve())))
if view.status() == QQuickView.Error:
sys.exit(-1)
view.resize(640, 480)
view.show()
for i in range(20):
item = QStandardItem(f"item-{i}")
manager.model.appendRow(item)
app.exec()
I have an application with an authentification page and then a homepage with a logoutButton. When I press on the logoutButton, it closes the page and put me in the authentication page, so it seems to work correctly. But when I do it again, I can't log in anymore, I am blocked on the authentication page.
Here is my code :
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
import QtQuick.Layouts 1.3
import "custom"
ApplicationWindow{
id: windowApp
//On expose notre stackview a tous les autres qml
property alias stack: stackView
width: 760
height: 500
minimumHeight: 500
minimumWidth: 700
visible: true
title: qsTr("App Home")
color:"#333333"
CustomButton {
id: logoutButton
text: qsTr("Déconnexion")
…
onClicked: {
logoutFunction()
//stackView.push(Qt.resolvedUrl("pages/"+returnpath2))
}
}
StackView{
id:stackView
anchors ……
initialItem: Qt.resolvedUrl("pages/accueil.qml")
}
function logoutFunction() {
var component = Qt.createComponent("main.qml")
var win2 = component.createObject()
windowApp.destroy()
win2.show()
}
}
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
ApplicationWindow{
id: window
width: 400
height: 550
visible: true
title: qsTr("Login Page")
color: "#333333"
// SET FLAGS
flags: Qt.WindowCloseButtonHint | Qt.WindowMinimizeButtonHint | Qt.CustomizeWindowHint | Qt.MSWindowsFixedSizeDialogHint | Qt.WindowTitleHint
// SET MATERIAL STYLE
Material.theme: Material.System
Material.accent: Material.Black
// CREATE TOP BAR
Rectangle{
id: topBar
height: 40
color: Material.accentColor
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: 10
anchors.leftMargin: 10
anchors.topMargin: 30
radius: 10
Text{
text: qsTr("Outil d'Analyse des trames IP des RX/TX 2G")
font.capitalization: Font.AllUppercase //mettre en maj
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.HorizontalFit
color: "#ffffff"
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 15
}
}
// IMAGE IMPORT
Image{
id: image
width: 300
height: 120
source: "../images/finland.jpeg"
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: topBar.bottom
anchors.topMargin: 60
}
// TEXT FIELD USERNAME
TextField{
id: usernameField
width: 300
text: qsTr("")
selectByMouse: true
placeholderText: qsTr("Your username or email")
verticalAlignment: Text.AlignVCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: image.bottom
anchors.topMargin: 60
onAccepted:{ //permet d'utiliser clavier entrée pour mdp
console.log("TEST")
backend.checkLogin(usernameField.text, passwordField.text)
}
}
// TEXT FIELD USERNAME
TextField{
id: passwordField
width: 300
text: qsTr("")
selectByMouse: true
placeholderText: qsTr("Your password")
verticalAlignment: Text.AlignVCenter
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: usernameField.bottom
anchors.topMargin: 10
echoMode: TextInput.Password
onAccepted:{ //permet d'utiliser clavier entrée pour mdp
console.log("TEST2")
backend.checkLogin(usernameField.text, passwordField.text)
}
}
// BUTTON LOGIN
Button{
id: buttonLogin
width: 300
implicitHeight: 40
text: qsTr("Login")
anchors.top: passwordField.bottom
anchors.topMargin: 40
anchors.horizontalCenter: parent.horizontalCenter
onClicked:
{
console.log("TEST3")
backend.checkLogin(usernameField.text, passwordField.text)
}
}
Connections {
target: backend
// CUSTOM PROPERTIES
property string username: ""
property string password: ""
function onSignalUser(myUser){ username = myUser }
function onSignalPass(myPass){ password = myPass }
// FUNTION OPEN NEW WINDOW (APP WINDOW)
function onSignalLogin(boolValue) {
if(boolValue){
var component = Qt.createComponent("app.qml")
var win = component.createObject()
window.close()
win.show()
} else{
// CHANGE USER COLOR
usernameField.Material.foreground = Material.Pink
usernameField.Material.accent = Material.Pink
passwordField.Material.foreground = Material.Pink
passwordField.Material.accent = Material.Pink
}
}
}
}
import sys
import os
# IMPORT MODULES
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal
# Main Window Class
class MainWindow(QObject):
def __init__(self):
QObject.__init__(self)
# Static Info
staticUser = ""
staticPass = ""
# Signals To Send Data
signalUser = Signal(str)
signalPass = Signal(str)
signalLogin = Signal(bool)
# Function To Check Login
#Slot(str, str)
def checkLogin(self, getUser, getPass):
if(self.staticUser.lower() == getUser.lower() and self.staticPass == getPass):
# Send Login Signal
self.signalLogin.emit(True)
print("Login passed!")
else:
self.signalLogin.emit(False)
print("Login error!")
# INSTACE CLASS
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
# Get Context
main = MainWindow()
engine.rootContext().setContextProperty("backend", main)
# Load QML File
engine.load(os.path.join(os.path.dirname(__file__), "qml/main.qml"))
# Check Exit App
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
You are complicating the application by creating many signals unnecessarily, instead create a qproperty that is read-only in QML, then that property will indicate if you have logged in or not and if you are not logged in you should go to the login page.
├── main.py
└── qml
├── main.qml
└── pages
├── index.qml
└── login.qml
main.py
import os
from pathlib import Path
import sys
from PySide2.QtCore import Property, QObject, Signal, Slot
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
class Controller(QObject):
logged_changed = Signal(name="loggedChanged")
_is_logged = False
#Property(bool, notify=logged_changed)
def logged(self):
return self._is_logged
def change_state(self, is_logged):
if self._is_logged == is_logged:
return
self._is_logged = is_logged
self.logged_changed.emit()
#Slot(str, str)
def login(self, username, password):
self.change_state(username == "username" and password == "password")
#Slot()
def logout(self):
self.change_state(False)
def main():
app = QGuiApplication(sys.argv)
controller = Controller()
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("controller", controller)
engine.load(os.fspath(Path(__file__).resolve().parent / "qml" / "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
StackView {
id: stackview
anchors.fill: parent
initialItem: Qt.resolvedUrl("pages/login.qml")
}
Connections{
target: controller
function onLoggedChanged(){
if(controller.logged){
stackview.pop()
stackview.push("pages/index.qml")
}
else{
stackview.pop()
stackview.push("pages/login.qml")
}
}
}
}
login.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Item {
ColumnLayout {
anchors.centerIn: parent
TextField {
id: username
placeholderText: "username"
}
TextField {
id: password
echoMode: TextInput.Password
placeholderText: "password"
}
Button {
id: login
text: "Login"
Layout.alignment: Qt.AlignCenter
onClicked: controller.login(username.text, password.text)
}
}
}
index.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
Item {
Button {
id: login
text: "Logout"
anchors.centerIn: parent
onClicked: controller.logout()
}
}
I want to send data from the textfield to the backend which is the main.py file. A function will then concatenate the string "Welcome" by adding what the input was in the textfield. This string will then be displayed in a label which is found in a third file through a stackpush on the main page. After connecting the Backend and front end my program only displays Welcome without the textfield input
main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
width: 640
height: 480
visible: true
color: "#00000000"
title: qsTr("Hello World")
Rectangle {
id: rectangle
color: "#ffffff"
anchors.fill: parent
StackView {
id: stackView
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: button.top
anchors.rightMargin: 10
anchors.leftMargin: 10
anchors.bottomMargin: 5
anchors.topMargin: 5
}
Button {
id: button
x: 368
y: 396
text: qsTr("Button")
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: 10
anchors.bottomMargin: 5
onClicked: stackView.push("home.qml")
onPressed: {
backend.welcomeText(txtName.text)
}
}
TextField {
id: txtName
x: 92
y: 436
placeholderText: qsTr("Text Field")
}
}
Connections {
target: backend
function onGetName(name){
welcomeLabel.text = name
}
}
}
/*##^##
Designer {
D{i:0;formeditorZoom:0.75}D{i:2}D{i:1}
}
##^##*/
home.qml
import QtQuick 2.0
import QtQuick.Controls 2.15
Item {
Rectangle {
id: rectangle
color: "#262626"
anchors.fill: parent
Label {
id: welcomeLabel
x: 251
y: 204
width: 251
height: 82
color: "#e9eaeb"
text: qsTr("Welcome")
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
main.py
import sys
import os
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, Slot, Signal
class MainWindow(QObject):
def __init__(self):
QObject.__init__(self)
#getName
getName = Signal(str)
#Slot(str)
def welcomeText(self, name):
self.getName.emit("Welcome " + name)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
main = MainWindow()
engine.rootContext().setContextProperty("backend", main)
engine.load(os.path.join(os.path.dirname(__file__), "main.qml"))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
The main problem is that welcomeLabel only has a scope of "home.qml" so it is not defined in main.qml.
Since "backend" is a contextProperty it has a global scope (something like a global variable) so you must make the connection of the "getName" signal in home.qml. But the problem is that you are emitting the signal before the page is loaded so the connection will not work, in this case the solution is to first load and then invoke the slot.
The solution in your practical case:
Move the "Connections" code from main.qml to home.qml
and change(remove pressed):
onClicked: {
stackView.push("home.qml")
backend.welcomeText(txtName.text)
}
Hi i have the following problem :
this is my working code
import sys
from PyQt5.QtCore import QObject, QUrl, Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
import os
import vlc
from time import sleep
# define VLC instance
instance = vlc.Instance()
# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')
player = instance.media_player_new()
list_test = []
list_name = []
def prepare_url(url):
media = instance.media_new(url)
player.set_media(media)
if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("main", engine)
engine.load('SimpleQML.qml')
win = engine.rootObjects()[0]
win.show()
button = win.findChild(QObject, "playBtn")
def myFunction():
print("A fine piece of text")
button.clicked.connect(myFunction) # works on click
myFunction() #works with out clicking
sys.exit(app.exec_())
Now i would like to expand on that by doing the following code :
import sys
from PyQt5.QtCore import QObject, QUrl, Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
import os
import vlc
from time import sleep
# define VLC instance
instance = vlc.Instance()
# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')
player = instance.media_player_new()
list_test = []
list_name = []
def prepare_url(url):
media = instance.media_new(url)
player.set_media(media)
if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("main", engine)
engine.load('SimpleQML.qml')
win = engine.rootObjects()[0]
win.show()
button = win.findChild(QObject, "playBtn")
comboBox = win.findChild(QObject, "comboBox")
def myFunction():
print("das")
def list_fill():
with open("config/stations.txt") as f:
content = f.readlines()
content = [x.strip() for x in content]
list_t = [item.split("|")[0] for item in content if item]
list_n = [item.split("|")[1] for item in content if item]
del list_test[:]
del list_name[:]
comboBox.clear()
for x in list_t:
list_test.append(x)
for x in list_n:
list_name.append(x)
addItems(list_name)
button.clicked.connect(myFunction) # works too
myFunction()
list_fill() #calling this crashes program
sys.exit(app.exec_())
and at the very end this is the error
das
Traceback (most recent call last):
File "/home/flea/Desktop/quick qt/main.py", line 65, in <module>
list_fill()
File "/home/flea/Desktop/quick qt/main.py", line 55, in list_fill
comboBox.clear()
AttributeError: 'QObject' object has no attribute 'clear'
i tried to do ti with hardcoded list, but list is not the problem, for some reason my combo box is not recognized by python.I am not sure what is the problem here.
I can load my button and add a click event to it, (which works), but i cant add list to my comboBox.
here is my Qml
import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3
ApplicationWindow {
id: applicationWindow
Material.theme: Material.Light
title: qsTr("Test Invoke")
width: 600
height: 500
Row {
id: row
width: 200
height: 400
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
Button{
id: playBtn
objectName: "playBtn"
text : "Play"
checked: false
padding: 6
rightPadding: 8
font.wordSpacing: 0
font.pointSize: 10
font.family: "Times New Roman"
topPadding: 4
highlighted: true
Material.accent: Material.Red
}
Button {
id: stopBtn
objectName: "stopBtn"
text: qsTr("Stop")
anchors.left: playBtn.right
anchors.leftMargin: 5
}
Button {
id: stfBtn
text: qsTr("Save")
objectName: "stfBtn"
anchors.left: stopBtn.right
anchors.leftMargin: 5
}
Button {
id: minimize
objectName: "minBtn"
text: qsTr("Min")
anchors.left: stfBtn.right
anchors.leftMargin: 5
}
}
Column {
id: column
x: 135
y: 100
width: 200
height: 400
TextField {
objectName: "nameText"
id: nameText
width: 300
text: qsTr("")
}
TextField {
objectName: "urlText"
id: urlText
width: 300
text: qsTr("")
}
ComboBox {
objectName: "comboBox"
id: comboBox
width: 200
}
}
Slider {
id: slide
objectName: "slider"
x: 160
y: 311
value: 0.5
}
}
It is not a good nor maintainable over time to instantiate an object created in QML from Python or C++.
The appropriate thing is to create an object in Python or C++ and send it to QML, and then create qproperties and slots that allow interacting with the QML.
In your case, I guess that list_fill tries to add data to the ComboBox, but the ComboBox does not have a clear method, so if you want to clean it, just pass it an empty list, or in your case pass it the new list.
On the other hand it is not elegant to call show(), it is best to set the visible property of ApplicationWindow to true.
main.py
import sys
import os
from PyQt5.QtCore import QObject, QUrl, Qt, pyqtSlot, pyqtSignal, pyqtProperty
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
import vlc
# define VLC instance
instance = vlc.Instance()
# Define VLC player
instance = vlc.Instance('--input-repeat=-1', '--fullscreen')
player = instance.media_player_new()
list_test = []
list_name = []
def prepare_url(url):
media = instance.media_new(url)
player.set_media(media)
class Manager(QObject):
stationsChanged = pyqtSignal()
currentStationChanged = pyqtSignal()
def __init__(self):
QObject.__init__(self)
self.m_stations = []
self.m_currentStation = ""
self.currentStationChanged.connect(self.on_currentStationChanged)
#pyqtProperty(str, notify=currentStationChanged)
def currentStation(self):
return self.m_currentStation
#currentStation.setter
def currentStation(self, val):
if self.m_currentStation == val:
return
self.m_currentStation = val
self.currentStationChanged.emit()
#pyqtProperty(list, notify=stationsChanged)
def stations(self):
return self.m_stations
#stations.setter
def stations(self, val):
if self.m_stations == val:
return
self.m_stations = val[:]
self.stationsChanged.emit()
#pyqtSlot()
def play(self):
print("play", self.currentStation)
#pyqtSlot()
def stop(self):
print("stop")
#pyqtSlot()
def on_currentStationChanged(self):
print(self.currentStation)
def list_fill(self):
l = []
with open("config/stations.txt") as f:
content = f.readlines()
content = [x.strip() for x in content]
list_t = [item.split("|")[0] for item in content if item]
list_n = [item.split("|")[1] for item in content if item]
l += list_t + list_n
self.stations = l
if __name__ == "__main__":
os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
manager = Manager()
ctx = engine.rootContext()
ctx.setContextProperty("Manager", manager)
engine.load('main.qml')
if not engine.rootObjects():
sys.exit(-1)
manager.list_fill()
sys.exit(app.exec_())
main.qml
import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3
ApplicationWindow {
id: applicationWindow
Material.theme: Material.Light
title: qsTr("Test Invoke")
visible: true
width: 600
height: 500
Row {
id: row
width: 200
height: 400
anchors.left: parent.left
anchors.leftMargin: 5
anchors.top: parent.top
anchors.topMargin: 5
Button{
id: playBtn
text : "Play"
checked: false
padding: 6
rightPadding: 8
font.wordSpacing: 0
font.pointSize: 10
font.family: "Times New Roman"
topPadding: 4
highlighted: true
Material.accent: Material.Red
onClicked: Manager.play()
}
Button {
id: stopBtn
text: qsTr("Stop")
anchors.left: playBtn.right
anchors.leftMargin: 5
onClicked: Manager.stop()
}
Button {
id: stfBtn
text: qsTr("Save")
objectName: "stfBtn"
anchors.left: stopBtn.right
anchors.leftMargin: 5
}
Button {
id: minimize
objectName: "minBtn"
text: qsTr("Min")
anchors.left: stfBtn.right
anchors.leftMargin: 5
}
}
Column {
id: column
x: 135
y: 100
width: 200
height: 400
TextField {
id: nameText
width: 300
text: qsTr("")
}
TextField {
id: urlText
width: 300
text: qsTr("")
}
ComboBox {
id: comboBox
width: 200
model: Manager.stations
onCurrentTextChanged: Manager.currentStation = currentText
}
}
Slider {
id: slider
x: 160
y: 311
value: 0.5
}
}
The advantage of this implementation is that you can modify the design and logic part independently. If you pass an object through setContextProperty it will be visible in all .qml files. With your previous approach you were going to have problems if you were going to have many .qml