I am trying to make QML form for controlling a Servo. So I made a QML form using the QT creator and loaded it using PYQT. From the QML form, I am trying to read a slider value to control the servo. Every time I try to move the slider it says:
AttributeError: 'NoneType' object has no attribute 'value' Aborted
(core dumped)
Here is my pyqt code:
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
dir_path = os.path.dirname(os.path.realpath(__file__))
class MainWindow(QtQml.QQmlApplicationEngine):
def __init__(self):
super(QtQml.QQmlApplicationEngine, self).__init__()
self.load(QtCore.QUrl.fromLocalFile(os.path.join(dir_path, "QML-1.0.qml")))
self.rootContext().setContextProperty("MainWindow", self)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.run)
self.timer.start(10)
if self.rootObjects():
# Inherit items from the GUI
self.window = self.rootObjects()[0]
self.text = self.window.findChild(QtCore.QObject, "textField")
self.slider = self.window.findChild(QtCore.QObject, "slider")
def run(self):
print (type(self.slider))
pass
#QtCore.pyqtProperty(int)
def rangeValue(self):
x = self.slider.value()
print x
return 10
if __name__ == '__main__':
app = QtGui.QGuiApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
And here is my qml code:
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Universal 2.0
import QtGraphicalEffects 1.0
ApplicationWindow {
id: root
width: 900
height: 300
opacity: 1
title: "window"
visible: true
//visibility: Window.FullScreen
visibility: Window.Maximized
Dial {
id: dial
x: 77
y: 60
width: 102
height: 103
wheelEnabled: true
}
Slider {
id: slider
x: 28
y: 220
value: 0.5
onValueChanged: MainWindow.rangeValue(value)
}
Label {
id: label
x: 64
y: 16
width: 128
height: 24
text: qsTr("Servo-1")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
Any help would be appreciated.
Thanks
It seems that you are trying to access a QML element from python which is considered a bad practice so I will not explain where the cause of the error is but I will propose a more stable and recommended solution: Create a QObject, export it to QML and update those properties from QML towards python.
import os
import sys
from PyQt5 import QtCore, QtGui, QtQml
DIR_PATH = os.path.dirname(os.path.realpath(__file__))
class ServoController(QtCore.QObject):
valueChanged = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(ServoController, self).__init__(parent)
self._value = 0
self.valueChanged.connect(self.process_value)
#QtCore.pyqtProperty(float, notify=valueChanged)
def value(self):
return self._value
#value.setter
def value(self, v):
self._value = v
self.valueChanged.emit()
def process_value(self):
print(self.value)
def main():
app = QtGui.QGuiApplication(sys.argv)
engine = QtQml.QQmlApplicationEngine()
servo_controller = ServoController()
engine.rootContext().setContextProperty("servo_controller", servo_controller)
url = QtCore.QUrl.fromLocalFile(os.path.join(DIR_PATH, "QML-1.0.qml"))
engine.load(url)
def on_object_created(obj, objUrl):
if obj is None and url == objUrl:
QtCore.QCoreApplication.exit(-1)
engine.objectCreated.connect(on_object_created, QtCore.Qt.QueuedConnection)
sys.exit(app.exec_())
if __name__ == "__main__":
main()
import QtQuick 2.0
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
import QtQuick.Controls.Universal 2.0
import QtGraphicalEffects 1.0
ApplicationWindow {
id: root
width: 900
height: 300
opacity: 1
title: "window"
visible: true
//visibility: Window.FullScreen
visibility: Window.Maximized
Dial {
id: dial
x: 77
y: 60
width: 102
height: 103
wheelEnabled: true
}
Slider {
id: slider
x: 28
y: 220
value: 0.5
onValueChanged: servo_controller.value = value
Component.onCompleted: servo_controller.value = value
}
Label {
id: label
x: 64
y: 16
width: 128
height: 24
text: qsTr("Servo-1")
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}
In the previous code I created the ServoController class whose function is to map the properties of QML to python such as the value of the slider and call the function process_value when that value changes.
In QML I update the ServoController property when the value of the slider changes and at startup.
Related
I would like to change a text in my gui. I can address the ListView element for which I have found a solution.
I would also like to address the text element arbitrarily and, for example, replace the text.
Also, I can't manage to address another ListView element with different content.
Here is my little program. Please excuse my English too
main.py
import sys
import vensoft_main
from time import sleep
import urllib.request
from pathlib import Path
from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtQuick import QQuickView
from PySide6.QtCore import QStringListModel, QUrl
from PySide6.QtGui import QGuiApplication
from PySide6.QtWidgets import QApplication
import json
import sys
from typing import Text
import urllib.request
import json
import pandas as pd
import random
if __name__ == '__main__':
# get our data
url = "file:///Users/joerg/Documents/python/Rohertrag/rohertrag_fenster/output_heute.json"
response = urllib.request.urlopen(url)
data = json.loads(response.read().decode('utf-8'))
# Format and sort the data
data_list = list(data.values())
# Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView)
view.update()
# Expose the list to the Qml code
my_model = QStringListModel()
my_model.setStringList(data_list)
view.setInitialProperties({"myModel": my_model})
# Load the QML file
qml_file = Path(__file__).parent / "view.qml"
view.setSource(QUrl.fromLocalFile(qml_file.resolve()))
# Show the window
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
view.update()
# execute and cleanup
app.exec()
del view
new.qml
import QtQuick
import QtQuick.Controls
Page {
width: 640
height: 480
Rectangle {
id: root
anchors.fill: parent
ListView {
id: view_1
anchors.fill: parent
anchors.margins: 25
anchors.bottomMargin: 230
anchors.rightMargin: 375
model: manager.model
delegate: Text {
anchors.leftMargin: 50
font.pointSize: 15
horizontalAlignment: Text.AlignHCenter
text: display
}
}
Text {
id: text1
x: 486
y: 46
width: 127
height: 118
text: manager.text
font.pixelSize: 12
}
ListView {
id: view_2
anchors.fill: parent
anchors.margins: 25
anchors.leftMargin: 25
anchors.topMargin: 238
anchors.rightMargin: 375
delegate: Text {
text: display
horizontalAlignment: Text.AlignHCenter
anchors.leftMargin: 50
font.pointSize: 15
}
anchors.bottomMargin: 17
model: manager.model
}
Text {
id: text2
x: 479
y: 272
width: 127
height: 118
text: manager.text
font.pixelSize: 12
}
}
header: Label {
color: "#15af15"
text: qsTr("Wie ist den der Umsatz Heute?")
font.pointSize: 17
font.bold: true
font.family: "Arial"
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
padding: 10
}
}
Less is more, you don't need to place unnecessary imports, also you don't need data from external resources, a simple list is enough.
If you want to manipulate the QML view from python then it is better to create a QObject that has data as properties and export it to QML.
import sys
from pathlib import Path
from PySide6.QtCore import (
Property,
QDateTime,
QObject,
QStringListModel,
QTimer,
QUrl,
Signal,
)
from PySide6.QtQuick import QQuickView
from PySide6.QtGui import QGuiApplication
class Manager(QObject):
text_changed = Signal(name="textChanged")
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStringListModel()
self._text = ""
#Property(QObject, constant=True)
def model(self):
return self._model
#Property(str, notify=text_changed)
def text(self):
return self._text
#text.setter
def text(self, text):
if self.text == text:
return
self._text = text
self.text_changed.emit()
def main():
# get our data
data_list = ["foo", "bar", "baz"]
manager = Manager()
# Set up the application window
app = QGuiApplication(sys.argv)
view = QQuickView()
view.rootContext().setContextProperty("manager", manager)
view.setResizeMode(QQuickView.SizeRootObjectToView)
manager.model.setStringList(data_list)
manager.text = "text"
qml_file = Path(__file__).parent / "view.qml"
view.setSource(QUrl.fromLocalFile(qml_file.resolve()))
if view.status() == QQuickView.Error:
sys.exit(-1)
view.show()
def handle_timeout():
text = QDateTime.currentDateTime().toString()
manager.text = text
timer = QTimer(interval=1000, timeout=handle_timeout)
timer.start()
app.exec()
del view
if __name__ == "__main__":
main()
import QtQuick
import QtQuick.Controls
Page {
width: 640
height: 480
Rectangle {
id: root
anchors.fill: parent
ListView {
id: view_1
anchors.fill: parent
anchors.margins: 25
anchors.rightMargin: 375
model: manager.model
delegate: Text {
anchors.leftMargin: 50
font.pointSize: 15
horizontalAlignment: Text.AlignHCenter
text: display
}
}
Text {
id: text1
x: 486
y: 46
width: 127
height: 201
text: manager.text
font.pixelSize: 12
}
}
header: Label {
color: "#15af15"
text: qsTr("Wie ist den der Umsatz Heute?")
font.pointSize: 17
font.bold: true
font.family: "Arial"
renderType: Text.NativeRendering
horizontalAlignment: Text.AlignHCenter
padding: 10
}
}
I am trying to get my .qml map to display a circle where the user clicks however keep tying myself in knots. The window output the coordinates to the spinboxes in GeoWidget properly, however I want a circle to appear in that location so the user has some feedback.
coord_map.qml
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Universal 2.0
import Fluid.Controls 1.0
import QtLocation 5.6
import QtPositioning 5.6
Item {
width: 600
height: 600
Map {
id: map
anchors.fill: parent
zoomLevel: 6
center: (0, 0)
MapItemView {
model: src
delegate: MapQuickItem {
coordinate: src.position.coordinate
anchorPoint.x: image.width * 0.5
anchorPoint.y: image.height
sourceItem: Column {
Image { id: image; source: "marker.png" }
Text { text: title; font.bold: true }
}
}
}
MapCircle {
id: circle
radius: parent.width/10
border.width: 1
color: 'green'
}
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {controller.update_from_map(map.toCoordinate(Qt.point(mouse.x, mouse.y)))
circle.center(map.toCoordinate(Qt.point(mouse.x, mouse.y)))
}
plugin: Plugin {
name: "osm"
}
}
}
coordbox.py
import os
import sys
from pathlib import Path
from PySide2.QtCore import Property, Signal, Slot, Qt, QUrl
from PySide2.QtPositioning import QGeoCoordinate
from PySide2.QtQuickWidgets import QQuickWidget
from PySide2.QtWidgets import (
QApplication,
QDialog,
QDialogButtonBox,
QDoubleSpinBox,
QHBoxLayout,
QLabel,
QToolButton,
QVBoxLayout,
QWidget,
QWizard,
QWizardPage,
)
CURRENT_DIRECTORY = Path(__file__).resolve().parent
# TODO: Give the user feedback on where their coordinate is when clicked
class MapDialog(QDialog):
def __init__(self, geo_widget):
super().__init__(geo_widget)
self.setWindowTitle("Map")
self.map_widget = QQuickWidget(resizeMode=QQuickWidget.SizeRootObjectToView)
self.map_widget.rootContext().setContextProperty("controller", geo_widget)
filename = os.fspath(CURRENT_DIRECTORY / "coord_map.qml")
url = QUrl.fromLocalFile(filename)
self.map_widget.setSource(url)
button_box = QDialogButtonBox()
button_box.setOrientation(Qt.Horizontal)
button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
lay = QVBoxLayout(self)
lay.addWidget(self.map_widget)
lay.addWidget(button_box)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
class GeoWidget(QWidget):
coordinate_changed = Signal(name="coordinateChanged")
def __init__(self, parent=None):
super().__init__(parent)
self._coordinate = QGeoCoordinate(0, 0)
self._lat_spinbox = QDoubleSpinBox(
minimum=49.0, maximum=56.0 # , valueChanged=self.handle_value_changed,
)
self._lng_spinbox = QDoubleSpinBox(
minimum=-8, maximum=2 # , valueChanged=self.handle_value_changed,
)
self.btn = QToolButton(text="Map", clicked=self.handle_clicked)
self.map_view = MapDialog(self)
# self.coordinate_changed.connect(self.handle_value_changed)
# self.coordinate_changed.connect(self.handle_value_changed)
lay = QHBoxLayout(self)
lay.addWidget(QLabel("Latitude:"))
lay.addWidget(self._lat_spinbox)
lay.addWidget(QLabel("Longitude:"))
lay.addWidget(self._lng_spinbox)
lay.addWidget(self.btn)
#Property(QGeoCoordinate, notify=coordinate_changed)
def coordinate(self):
return self._coordinate
#coordinate.setter
def coordinate(self, coordinate):
if self.coordinate == coordinate:
return
self._coordinate = coordinate
self.coordinate_changed.emit()
def handle_value_changed(self):
coordinate = QGeoCoordinate(
self._lat_spinbox.value(), self._lng_spinbox.value()
)
self.coordinate = coordinate
#Slot(QGeoCoordinate)
def update_from_map(self, coordinate):
self.coordinate = coordinate
self._lat_spinbox.setValue(self.coordinate.latitude())
self._lng_spinbox.setValue(self.coordinate.longitude())
def handle_clicked(self):
self.map_view.exec_()
# Rest of code is used to debug this coordinate box
class WizardPage(QWizardPage):
def __init__(self, parent=None):
super().__init__(parent)
self.geo_widget1 = GeoWidget()
self.geo_widget2 = GeoWidget()
self.registerField("coordinate1", self.geo_widget1)
self.registerField("coordinate2", self.geo_widget2)
lay = QVBoxLayout(self)
lay.addWidget(self.geo_widget1)
lay.addWidget(self.geo_widget2)
def main():
app = QApplication(sys.argv)
w = QWizard()
page = WizardPage()
w.addPage(page)
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I have had it working however it just shows a blank screen now I am attempting to implement the feedback. I have tried to pick up as much as I can from the documents but not being able to debug qml is causing issues as I don't even know if onClicked can do two things at once (eg. emit the coordinates to the spinboxes and add a feedback circle.
Your code has several things not defined so I will delete them. On the other hand, center is a property, it is not a function, so what you must do is assign the position. It should also be noted that the radius of MapCircle is in meters and not in pixels, so you must choose an appropriate value for the zoom you use.
import QtLocation 5.6
import QtPositioning 5.6
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Controls.Material 2.0
import QtQuick.Controls.Universal 2.0
Item {
width: 600
height: 600
Map {
id: map
anchors.fill: parent
zoomLevel: 6
center: QtPositioning.coordinate(0, 0)
MapCircle {
id: circle
radius: 100000
border.width: 1
color: 'green'
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
var pos = map.toCoordinate(Qt.point(mouse.x, mouse.y));
circle.center = pos;
controller.update_from_map(pos);
}
}
plugin: Plugin {
name: "osm"
}
}
}
I have defined a button (actually it is a MouseArea) in QML inside an ApplicationWindow. I managed to connect to its clicked event from PyQt5. Now I am trying to show a save file dialog, but I get the error:
QWidget: Cannot create a QWidget without QApplication
My code looks like this:
from PyQt5.QtCore import QUrl, QObject # pylint: disable-msg=E0611
from PyQt5.QtGui import QGuiApplication, QIcon # pylint: disable-msg=E0611
from PyQt5.QtQml import QQmlApplicationEngine # pylint: disable-msg=E0611
from PyQt5.QtWidgets import QFileDialog # pylint: disable-msg=E0611
def openFile():
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
filename = QFileDialog.getOpenFileName(None,"QFileDialog.getOpenFileName()", "","All Files (*);;Python Files (*.py)", options=options)
print(filename)
def run():
app = QGuiApplication(sys.argv)
app.setWindowIcon(QIcon(resource_path("assets\\images\\icon.ico")))
engine = QQmlApplicationEngine()
engine.load(resource_path("qml\\Window.qml"))
engine.quit.connect(app.quit)
if not engine.rootObjects():
return -1
button = engine.rootObjects()[0].findChild(QObject, "openButton")
button.clicked.connect(openFile)
return app.exec_()
if __name__ == '__main__':
sys.exit(run())
I have also tried to pass the ApplicationWindow instead of None, but then I get a type error:
TypeError: getOpenFileName(parent: QWidget = None, caption: str = '', directory: str = '', filter: str = '', initialFilter: str = '', options: Union[QFileDialog.Options, QFileDialog.Option] = 0): argument 1 has unexpected type 'QWindow'
which does not make a lot of sense in my opinion, because QMainWindow inherits QWidget.
How can I show the dialog from my openFile() function?
EDIT: For the sake of completeness here is a stripped down version of my .qml file
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick 2.3
ApplicationWindow {
id: mainWindow
visible: true
width: 600
height: 400
Item {
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
property bool isMainButtonFocused: false
objectName: "openButton"
signal clicked()
Label {
padding: 5
text: "<b><font color='#fefefe'>Hello World</font></b>"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.family: "Helvetica"
font.pointSize: 9
background: Rectangle {
color: mouseAreaOpenFolderButton.containsMouse ? "#777777" : "#333333"
border.width: isMainButtonFocused ? 2 : 1
border.color: "#ffffff"
radius: 5
}
MouseArea {
id: mouseAreaOpenFolderButton
anchors.fill: parent
hoverEnabled: true
onClicked: {
isMainButtonFocused = true
parent.parent.clicked()
}
}
}
}
}
The errors are very clear:
If you are going to use a QWidget like QFileDialog then you must create a QApplication.
If you are going to pass a parent a QWidget, as QFileDialog is, then that parent must be another QWidget but ApplicationWindow is not a QWidget but a QWindow causing that error.
Other additional but more important error is that you should not access the QML object from Python (or C++) since its life cycle is different so it could have problems (See this answer for example), instead create a QObject where implement the logic and expose it as an item (with qmlRegisterType) or a context property (via setContextProperty).
Considering the above then you should use FileDialog and expose a QObject as context property:
# ...
class Helper(QObject):
#pyqtSlot(QUrl)
def read_file(self, url):
filename = url.toLocalFile()
print(filename)
def run():
app = QGuiApplication(sys.argv)
app.setWindowIcon(QIcon(resource_path("assets\\images\\icon.ico")))
engine = QQmlApplicationEngine()
helper = Helper()
engine.rootContext().setContextProperty("helper", helper)
# ...
import QtQuick.Window 2.2
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQuick 2.3
import QtQuick.Dialogs 1.3
ApplicationWindow {
id: mainWindow
visible: true
width: 600
height: 400
Item {
anchors.top: titleBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
property bool isMainButtonFocused: false
Label {
padding: 5
text: "<b><font color='#fefefe'>Hello World</font></b>"
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.family: "Helvetica"
font.pointSize: 9
background: Rectangle {
color: mouseAreaOpenFolderButton.containsMouse ? "#777777" : "#333333"
border.width: isMainButtonFocused ? 2 : 1
border.color: "#ffffff"
radius: 5
}
MouseArea {
id: mouseAreaOpenFolderButton
anchors.fill: parent
hoverEnabled: true
onClicked: {
// isMainButtonFocused = true
fileDialog.visible = true
}
}
}
}
FileDialog {
id: fileDialog
title: "Please choose a file"
selectedNameFilter: "All Files (*);;Python Files (*.py)"
onAccepted: {
helper.read_file(fileDialog.fileUrls[0])
}
}
}
I'm trying to access a FileDialog control from the python file that starts the QQmlApplication engine in order to retrieve the file path property. I have set up a signal in the .qml file, however I cannot access the file dialog by id in the python file to set up the slot. The findChild method in application.py returns None. Here is the code:
application.py
import sys
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine, QQmlFileSelector
sys_argv = sys.argv
sys_argv += ['--style', 'material']
app = QGuiApplication(sys_argv)
window = QQmlApplicationEngine()
window.load("QML/main.qml")
fileDialog = window.findChild(QQmlFileSelector, "fileDialog")
print(fileDialog)
app.exec_()
Page1.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
Page {
width: 600
height: 400
header: Label {
text: qsTr("Prepare Data")
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Button {
text: qsTr("Load data")
anchors.centerIn: parent
onClicked: fileDialog.visible = true
padding: 10
}
signal folderSelected()
FileDialog {
id: fileDialog
selectFolder: true
title: qsTr("Select the data directory")
folder: shortcuts.home
onAccepted: {
parent.folderSelected()
}
}
}
main.qml
import QtQuick 2.0
import QtQuick.Controls 2.12
import QtQuick.Controls.Material 2.12
ApplicationWindow{
visible: true
title: qsTr("Main window")
width: 1000
height: 800
Material.theme: Material.Light
Material.accent: Material.Orange
SwipeView {
id: swipeView
anchors.fill: parent
Page1 {
}
Page2 {
}
Page3 {
}
}
}
In an old answer explain in the section Pushing References to QML how to update some python object from QML, that methodology is the one recommended by Qt and it is the one that should be used now. With your current method you need to establish an objectname that can be problematic in many cases.
So the solution is to create a QObject that we export to QML and update the qproperty, this will emit a signal that we connect to a slot where we can do the logic that we want. On the other hand FileDialog returns a url, so the property must be a QUrl:
main.qml
import os
import sys
from PySide2 import QtCore, QtGui, QtQml
class FileManager(QtCore.QObject):
file_url_Changed = QtCore.Signal(QtCore.QUrl)
def __init__(self, parent=None):
super(FileManager, self).__init__(parent)
self._file_url = QtCore.QUrl()
def get_file_url(self):
return self._file_url
def set_file_url(self, file_url):
if self._file_url != file_url:
self._file_url = file_url
self.file_url_Changed.emit(self._file_url)
file_url = QtCore.Property(QtCore.QUrl, fget=get_file_url, fset=set_file_url, notify=file_url_Changed)
#QtCore.Slot(QtCore.QUrl)
def on_file_url_changed(file_url):
print(file_url.toLocalFile())
if __name__ == '__main__':
sys.argv += ['--style', 'material']
app = QtGui.QGuiApplication(sys.argv)
file_manager = FileManager()
file_manager.file_url_Changed.connect(on_file_url_changed)
engine = QtQml.QQmlApplicationEngine()
engine.rootContext().setContextProperty("file_manager", file_manager)
file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "QML", "main.qml")
engine.load(QtCore.QUrl.fromLocalFile(file))
if not engine.rootObjects():
sys.exit(-1)
sys.exit(app.exec_())
Page1.qml
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Dialogs 1.2
Page {
width: 600
height: 400
header: Label {
text: qsTr("Prepare Data")
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Button {
text: qsTr("Load data")
anchors.centerIn: parent
onClicked: fileDialog.visible = true
padding: 10
}
FileDialog {
id: fileDialog
selectFolder: true
title: qsTr("Select the data directory")
folder: shortcuts.home
onAccepted: {
file_manager.file_url = fileDialog.fileUrl // <---
}
}
}
I’m beginner on programming in python and qml and I'm doing a project
My project need to have many UI forms I’m using qml to create these UI
Lets say we have Form A,B,C and when application load I need it to open Form A and form A contain button which I click and open Form B and Form A close, Form B it have button which I click it open form C and that form B close… Plis help me on these project
main.py
import sys
from PyQt5.QtCore import QObject, QUrl, Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtQml import QQmlApplicationEngine
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = QQmlApplicationEngine()
ctx = engine.rootContext()
ctx.setContextProperty("main", engine)
ctx2 = engine.rootContext()
ctx2.setContextProperty("main", engine)
engine.load('form1.qml')
win = engine.rootObjects()[0]
def pageC():
engine.load('form3.qml')
win2 = engine.rootObjects()[0]
button1 = win2.findChild(QObject, "form3")
button1.clicked.connect(pageC)
win2.show()
def newPage():
engine.load('form2.qml')
win = engine.rootObjects()[0]
win.show()
button1=win.findChild(QObject, "form2")
button1.clicked.connect(newPage)
win.show()
sys.exit(app.exec_())
form1.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.5
Window {
visible: true
width: 200
height: 200
title: qsTr("Hello World")
maximumHeight: 200
minimumHeight: 200
maximumWidth: 200
minimumWidth: 200
Button {
id: button1
objectName: "form2"
x: 22
y: 71
width: 157
height: 59
text: qsTr("Page a")
onClicked: callPageB()
}
}
form2.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.5
Window {
visible: true
width: 200
height: 200
title: qsTr("Hello World")
maximumHeight: 200
minimumHeight: 200
maximumWidth: 200
minimumWidth: 200
Button {
id: button1
objectName: "form3"
x: 22
y: 71
width: 157
height: 59
text: qsTr("Page B")
onClicked: callPageC()
}
}
form3.qml
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 1.5
Window {
visible: true
width: 200
height: 200
title: qsTr("Hello World")
maximumHeight: 200
minimumHeight: 200
maximumWidth: 200
minimumWidth: 200
Button {
id: button1
x: 22
y: 71
width: 157
height: 59
text: qsTr("Page C")
}
}
When adding qmls to QQmlApplicationEngine we can get through rootObjects(), so to do this I simply created the following class, but there are certain restrictions, each button inside the Window should have the objectName as the name of the file and must Be the children of Window:
example.qml:
Window {
...
Button {
objectName: "example"
}
...
}
In the next part this class with the name of your qmls.
class Engine(QQmlApplicationEngine):
counter = 0
def __init__(self, qmls, parent=None):
QQmlApplicationEngine.__init__(self, parent)
[self.load("{}.qml".format(qml)) for qml in qmls]
for i, root in enumerate(self.rootObjects()):
if root != self.rootObjects()[0]:
root.close()
if root != self.rootObjects()[-1]:
button= root.findChild(QObject, qmls[i])
button.clicked.connect(self.closeAndOpen)
def closeAndOpen(self):
self.rootObjects()[self.counter].close()
self.counter += 1
self.rootObjects()[self.counter].show()
if __name__ == "__main__":
app = QApplication(sys.argv)
engine = Engine(['form1', 'form2', 'form3'])
sys.exit(app.exec_())