I have to do a menu in QML that contains the list of items in a directory and, by clicking over one of them, it plays a video (player.play())
This is what I tried, but is not working
___.qml
Menu {
id: menu
contentItem: ListView {
model: ui.textMenu
}
}
Menu {
id: menu
y: -200
x: 100
Repeater{
model: ui.textMenu
MenuItem {
text: textMenu.entry
onClicked:{
player.source = textMenu.address
player.play()
}
} //x: model.x*(videoPlayer.width - 10)
}
}
___.py
self.__textMenu = QStandardItemModel(self)
menuRoles = {entryRole: b"entry", addressRole: b"address"}
self.__textMenu.setItemRoleNames(menuRoles)
def menu(self):
directoryPath = os.path.join(self.__currentPath, r"video" )
entries = os.listdir(directoryPath)
for entry in entries:
menuVoice = QStandardItem()
menuVoice.setData(entry, entryRole)
videoPath = os.path.join(directoryPath, entry)
menuVoice.setData(videoPath, addressRole)
Your QML seems inconsistent with what you want. In this case I have created a Menu within the MenuBar where the MenuItem will be created using the model and a Repeater:
import os
import sys
from PySide2 import QtCore, QtGui, QtQml
NameRole = QtCore.Qt.UserRole + 1000
PathRole = QtCore.Qt.UserRole + 1001
def create_model(dir_path):
model = QtGui.QStandardItemModel()
roles = {NameRole: b"name", PathRole: b"path"}
model.setItemRoleNames(roles)
for name in os.listdir(dir_path):
it = QtGui.QStandardItem()
it.setData(name, NameRole)
it.setData(os.path.join(dir_path, name), PathRole)
model.appendRow(it)
return model
def main(args):
app = QtGui.QGuiApplication(args)
current_dir = os.path.dirname(os.path.realpath(__file__))
video_dir = os.path.join(current_dir, "video")
model = create_model(video_dir)
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("video_model", model)
current_dir = os.path.dirname(os.path.realpath(__file__))
filename = os.path.join(current_dir, "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(filename))
if not engine.rootObjects():
return -1
ret = app.exec_()
return ret
if __name__ == "__main__":
sys.exit(main(sys.argv))
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtMultimedia 5.13
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
menuBar: MenuBar {
Menu {
id: plugins_menu
title: qsTr("&Videos")
Repeater{
model: video_model
MenuItem{
text: model.name
onTriggered: {
video.source = Qt.resolvedUrl(model.path)
video.play()
}
}
}
}
}
Video {
id: video
anchors.fill: parent
}
}
Related
I am using PySide2, Qt Quick, and Qt Creator.
I am trying to make it so that it uses a variable from the text field that is stored in the QML file and run a command that prints the variable into the console using print().
In the main.py file, I created this code that finds the price of assets of cryptocurrencies.
def assetSet(self):
asset = "xrp"
url2 = f'https://coinmarketcap.com/currencies/{asset}'
page2 = requests.get(url2)
soup2 = bs(page2.text, 'html.parser')
price2 = soup2.find(class_="priceValue___11gHJ")
price12 = str(price2)
result2 = re.search('<div class="priceValue___11gHJ">(.*)</div>', price12)
result12 = result2.group(1)
self.setAsset.emit(f"The price of {asset} is {result12}")
In the QML File, I have
CustomButton{
id: btnBdo
text: "Check"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: 200
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenterOffset: 0
font.pointSize: 20
colorMouseOver: "#21252d"
colorPressed: "#191d23"
colorDefault: "#2a303a"
onClicked: {
backend.assetSet()
}
}
CustomTextField {
id: textField
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenterOffset: -100
anchors.horizontalCenter: parent.horizontalCenter
font.pointSize: 20
placeholderText: qsTr("Enter a name")
objectName: "assetField"
}
I want the asset variable to equal the text field that is stored in the QML file and run this command when the button is clicked.
Is there a way to do this?
In this case it is best to use a slot that receives the data, and since the execution of the function is also synchronous then the signal is not necessary:
#Slot(str, result=str)
def assetSet(self, asset):
url2 = f'https://coinmarketcap.com/currencies/{asset}'
page2 = requests.get(url2)
soup2 = bs(page2.text, 'html.parser')
price2 = soup2.find(class_="priceValue___11gHJ")
price12 = str(price2)
result2 = re.search('<div class="priceValue___11gHJ">(.*)</div>', price12)
result12 = result2.group(1)
return f"The price of {asset} is {result12}"
onClicked: function(){
var resp = backend.assetSet(textField.text)
console.log(resp)
}
On the other hand, the request can block the Qt event loop causing the GUI to freeze so it is better to execute that task in another thread:
from functools import cached_property
import os.path
import re
import sys
import threading
import requests
from bs4 import BeautifulSoup as bs
from PySide2.QtCore import QObject, Qt, QUrl, Signal, Slot
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
class Worker(QObject):
finished = Signal(str)
def start(self, asset):
threading.Thread(target=self._execute, args=(asset,), daemon=True).start()
def _execute(self, asset):
url2 = f"https://coinmarketcap.com/currencies/{asset}"
page2 = requests.get(url2)
soup2 = bs(page2.text, "html.parser")
price2 = soup2.find(class_="priceValue___11gHJ")
price12 = str(price2)
result2 = re.search('<div class="priceValue___11gHJ">(.*)</div>', price12)
if result2 is not None:
result12 = result2.group(1)
self.finished.emit(f"The price of {asset} is {result12}")
return
self.finished.emit(f"failed for {asset}")
class Backend(QObject):
assetChanged = Signal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.worker.finished.connect(self.assetChanged)
#cached_property
def worker(self):
return Worker()
#Slot(str)
def assetSet(self, asset):
self.worker.start(asset)
def main():
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
backend = Backend()
engine.rootContext().setContextProperty("backend", backend)
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
file = os.path.join(CURRENT_DIR, "main.qml")
url = QUrl.fromLocalFile(file)
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_())
if __name__ == "__main__":
main()
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
width: 640
height: 480
visible: true
Column {
Button {
id: button
text: "Press me"
onClicked: function() {
backend.assetSet(textField.text);
}
}
TextField {
id: textField
placeholderText: qsTr("Enter a name")
}
Label {
id: label
}
}
Connections {
function onAssetChanged(asset) {
label.text = asset;
}
target: backend
}
}
I've been trying to make for example 10 different elements for example buttons and I could do that using the repeater but I've been having trouble with setting the text for each new element.
I'm getting the texts I want to set from a list in Python and I sent them to qml through QStringListModel. The text got to qml from the list as I wanted, but somehow the repeater set's the text of all the elements as the last string in the list given from Python.
I will provide the code later for extra explanation but for now I want to see if someone have idea how I can do it...(the thing is that the code is in another device and I'm waiting for it). Basically what I'm trying to do is, let's say for example, I have a list in python a_list = {buttonName, buttonAge, buttonHeight} and I want to make multiple buttons in qml as the size of the list (in this case 3) and change each text of the buttons i made in the repeater as the strings in the list).
this is main.py
import sys
from PySide2.QtCore import QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from foo import FooController
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
fooController = FooController()
engine.rootContext().setContextProperty("fooController", fooController)
engine.load(QUrl.fromLocalFile('main.qml'))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
This is foo.py
from PySide2.QtCore import QObject, Property, Slot
class x:
def __init__(self, name, age):
self.name = name
self.age = age
class FooController(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.__text = "Foo"
self.__name = s1.name
self.__age = s1.age
#Property(str)
def text(self):
return self.__name
#Slot()
def clickListener(self):
print(self.__name)
This is foo.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
Button {
text: fooController.text
onClicked: fooController.clickListener()
}
and here is the qml window that contains the repeater
import QtQuick 2.0
import "../components"
//import QtQuick.Timeline 1.0
import QtQuick.Controls 2.15
import QtQuick 2.15
import QtQuick.Window 2.15
import QtGraphicalEffects 1.15
import QtQuick.Layouts 1.15
import "../main.py"
window{
width: 1500
height: 920
minimumWidth: 1100
minimumHeight: 650
visible: true
color: "#00000000"
id: mainWindow
title: qsTr("--")
Rectangle{
id: rectangle
anchors.fill: parent
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
radius: 10
color: "#4642b6"
Flickable {
id: flickable
contentHeight: gridLayoutBottom.height
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.topMargin: 96
anchors.rightMargin: 8
anchors.leftMargin: 8
anchors.bottomMargin: 8
clip: true
ListModel {
id: imageModel
ListElement { _id: "tile0" }
}
Repeater {
model: imageModel
delegate: CustomMenuType{
ListView{
model: s
delegate: Text {
text: model.display
font.family: "Segoe UI"
color: "#ffffff"
}
}
//text: ListView.delegate.Text.text
font.pointSize: 9
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
}
}
}
}
Disclaimer: The code you provide is useless since it does not show any attempt to solve your problem, besides that there are undefined elements that I will not take as a basis for my answer, and it is a shame because you always learn more by correcting errors.
If you want to handle the information that the Repeater uses from Python then you must use a model. The Repeater supports 3 types of models:
A number,
A list or
An object that inherits from QAbstractItemModel.
In this case the first does not provide important information since it only indicates the number of elements so it will not show it since it is a trivial example.
In the case of the list the logic is to create a Q_PROPERTY of type "QVariantList" (in PyQt5 list is enough) and that has an associated signal that is emitted every time the list changes so that the view is notified and updates the painting.
import os.path
import sys
from PySide2.QtCore import Property, QObject, QDateTime, QTimer, QUrl, Signal
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
class Controller(QObject):
modelChanged = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self._model = ["Text1", "Text2", "Text3"]
#Property("QVariantList", notify=modelChanged)
def model(self):
return self._model
def update_model(self, l):
self._model = l[:]
self.modelChanged.emit()
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
controller = Controller()
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("controller", controller)
filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
def on_timeout():
dt = QDateTime.currentDateTime()
l = [dt.addSecs(i).toString() for i in range(3)]
controller.update_model(l)
timer = QTimer(timeout=on_timeout, interval=1000)
timer.start()
sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window{
width: 640
height: 480
visible: true
Column {
Repeater {
model: controller.model
Button {
text: model.modelData
}
}
}
}
In the case of QAbstractItemModel the logic is to create a QProperty of type QObject and make it constant since the model itself does not change but the information it manages. And on the QML side, the property must be accessed using the associated role, for example in the case of QStringListModel the role is "display":
import os.path
import sys
from PySide2.QtCore import Property, QDateTime, QObject, QStringListModel, QTimer, QUrl
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
class Controller(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStringListModel()
self.model.setStringList(["Text1", "Text2", "Text3"])
def get_model(self):
return self._model
model = Property(QObject, fget=get_model, constant=True)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
controller = Controller()
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("controller", controller)
filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
def on_timeout():
dt = QDateTime.currentDateTime()
l = [dt.addSecs(i).toString() for i in range(3)]
controller.model.setStringList(l)
timer = QTimer(timeout=on_timeout, interval=1000)
timer.start()
sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window{
width: 640
height: 480
visible: true
Column {
Repeater {
model: controller.model
Button {
text: model.display
}
}
}
}
You can also create a custom model but you have to declare a role associated with the data, a trivial example is to use the QStandardItemModel class.
import os.path
import sys
from PySide2.QtCore import (
Property,
QDateTime,
QObject,
Qt,
QTimer,
QUrl,
)
from PySide2.QtGui import QGuiApplication, QStandardItem, QStandardItemModel
from PySide2.QtQml import QQmlApplicationEngine
CURRENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
TEXT_ROLE = Qt.UserRole + 1000
DATA_ROLE = TEXT_ROLE + 1
class Controller(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStandardItemModel()
self.model.setItemRoleNames({TEXT_ROLE: b"text", DATA_ROLE: b"data"})
for i in range(3):
item = QStandardItem()
item.setData("Text{}".format(i), TEXT_ROLE)
item.setData(i, DATA_ROLE)
self.model.appendRow(item)
def get_model(self):
return self._model
model = Property(QObject, fget=get_model, constant=True)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
controller = Controller()
engine = QQmlApplicationEngine()
engine.rootContext().setContextProperty("controller", controller)
filename = os.path.join(CURRENT_DIRECTORY, "main.qml")
engine.load(QUrl.fromLocalFile(filename))
if not engine.rootObjects():
sys.exit(-1)
def on_timeout():
dt = QDateTime.currentDateTime()
for i in range(controller.model.rowCount()):
item = controller.model.item(i)
item.setData(dt.addSecs(i).toString(), TEXT_ROLE)
item.setData(dt.addSecs(i), DATA_ROLE)
timer = QTimer(timeout=on_timeout, interval=1000)
timer.start()
sys.exit(app.exec_())
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window{
width: 640
height: 480
visible: true
Column {
Repeater {
model: controller.model
Button {
text: model.text
onClicked: console.log(model.data)
}
}
}
}
You could also create a class that inherits from QAbstractListModel and overrides the data, setData, roleNames, rowCount methods.
I try to get the root object after window having completed, but I get a error:
QmlObj = self.engine.rootObjects()[0]
Error: list index out of range
The strange thing is that it works when I try to call foo.init_window() after the MouseArea having clicked.
Here is my python code:
main.py
from PySide2.QtWidgets import QApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtCore import QObject, QUrl, Slot
import sys
import win32gui
flag = False
class Foo(QObject):
def __init__(self):
super().__init__()
self.engine = QQmlApplicationEngine()
#Slot()
def init_window(self):
global flag
if not flag:
QmlObj = self.engine.rootObjects()[0]
desk = win32gui.FindWindow("Progman", "Program Manager")
print(desk)
sndWnd = win32gui.FindWindowEx(desk, 0, "SHELLDLL_DefView", None)
print(sndWnd)
targetWnd = win32gui.FindWindowEx(sndWnd,
0, "SysListView32", "FolderView")
print(targetWnd)
win32gui.SetParent((int)(QmlObj.winId()), targetWnd)
flag = True
if __name__ == "__main__":
app = QApplication(sys.argv)
foo = Foo()
foo.engine.rootContext().setContextProperty("foo", foo)
foo.engine.load(QUrl("main.qml"))
# win = foo.engine.rootObjects()[0]
# win.show()
if not foo.engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
Here is the .qml file:
main.qml
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
width: 200
height: 100
visible: true
//flags: Qt.FramelessWindowHint
//flags: Qt.WindowStaysOnBottomHint
//flags: Qt.WindowMinMaxButtonsHint
Rectangle {
anchors.fill: parent
color: "red"
Component.onCompleted: foo.init_window()
MouseArea {
anchors.fill: parent
onClicked: foo.init_window()
}
Text {
anchors.centerIn: parent
text: "Hello, World!"
}
Button {
text: "Ok"
onClicked: {
console.log("OK Button clicked....")
}
}
}
}
The problem is that in Component.onCompleted the window(the rootObject) has finished building but the engine list has not been updated. The solution is to invoke init_window an instant later using Qt.callLater():
Component.onCompleted: Qt.callLater(foo.init_window)
The following code creates a dialog. There is inconsistent behaviour happening between pressing the "Enter" key and hitting the "OK" button. When the enter key is pressed on changing a field, only that field is updated. When the OK button is pressed, both are updated (which is preferred). How can I override the Enter key to do the reasonable thing here?
What I would really like, is if the enter key sends the updated field back to the application without closing the dialog, since I would like to control something from within the dialog.
view.qml
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import QtQuick.Dialogs 1.3
Item {
Dialog {
id: thedialog
ColumnLayout {
TextField {
id: numberField
onAccepted: {
backend.number = text
}
}
TextField {
id: textField
onAccepted: {
backend.text = text
}
}
}
onButtonClicked: {
backend.number = numberField.text
backend.text = textField.text
}
}
Button {
text: "Show Dialog"
onClicked: thedialog.open()
}
}
main.py
import sys
from PySide2.QtCore import QObject, Signal, Property
from PySide2.QtWidgets import QApplication, QMainWindow
from PySide2.QtQuickWidgets import QQuickWidget
class Backend(QObject):
def __init__(self):
QObject.__init__(self)
self._number = 0
self._text = ""
def getNumber(self):
return self._number
def setNumber(self, number):
print(f"Setting number to: {number}")
self._number = number
self.notifyNumber.emit()
notifyNumber = Signal()
number = Property(float, getNumber, setNumber, notify=notifyNumber)
def getText(self):
return self._text
def setText(self, text):
print(f"Setting text to: {text}")
self._text = text
self.notifyText.emit()
notifyText = Signal()
text = Property(str, getText, setText, notify=notifyText)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = QMainWindow()
quick = QQuickWidget()
backend = Backend()
quick.rootContext().setContextProperty("backend", backend)
quick.setSource("view.qml")
window.setCentralWidget(quick)
window.show()
sys.exit(app.exec_())
Use the attached Keys API:
import QtQuick 2.14
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
ApplicationWindow {
width: 800
height: 600
visible: true
QtObject {
id: backend
property real number
property string text
}
Dialog {
id: thedialog
function doSomeStuffBeforeClosingTheDialog() {
backend.number = parseFloat(numberField.text)
backend.text = textField.text
// Remove this if you do not want the dialog to close
accept()
}
ColumnLayout {
TextField {
id: numberField
onAccepted: backend.number = text
Keys.onReturnPressed: thedialog.doSomeStuffBeforeClosingTheDialog()
}
TextField {
id: textField
onAccepted: backend.text = text
Keys.onReturnPressed: thedialog.doSomeStuffBeforeClosingTheDialog()
}
}
}
Button {
text: "Show Dialog"
onClicked: thedialog.open()
}
}
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