How show a glowing label in QMainWindow - python

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
}
}

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())

Python dynamically instantiate QML Components

I want to use Python to dynamically add custom components onto my view.qml, but I am not sure about my method because I can't see the Button.qml component in the resulting window. Ideally, I hope to be able to instantiate several rows of buttons into the ColumnLayout. By the way, Button.qml custom quick example/demo button whose's source code I have included too below. It is not QtQuick Button.qml from the PySide6 library
I thought I could just call functions from the view.qml but apparently not? I have seen another method that involves using a separate Javascript file, but I would like to avoid doing that if possible.
Main.py
import os
from pathlib import Path
import sys
from PySide6.QtCore import QUrl, QObject
from PySide6.QtGui import QGuiApplication
from PySide6.QtQuick import QQuickView
class CreateWidgets(QObject):
def instantiate_widgets(self, root, widgetsNeeded):
#for i in widgetsNeeded:
root.doSomething
if __name__ == '__main__':
app = QGuiApplication(sys.argv)
view = QQuickView()
view.setResizeMode(QQuickView.SizeRootObjectToView);
qml_file = os.fspath(Path(__file__).resolve().parent / 'view.qml')
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Error:
sys.exit(-1)
root = view.rootObject()
widgetCreator = CreateWidgets()
widgetCreator.instantiate_widgets(root, 6)
view.show()
res = app.exec()
# Deleting the view before it goes out of scope is required to make sure all child QML instances
# are destroyed in the correct order.
del view
sys.exit(res)
view.qml
import QtQuick 2.0
import QtQuick.Layouts 1.12
Item{
function doSomething(){
var component = Qt.createComponent("Button.qml");
if (component.status === Component.Ready) {
var button = component.createObject(colLayout);
button.color = "red";
}
console.log("Button created");
}
ColumnLayout{
id: colLayout
Rectangle {
id: page
width: 500; height: 200
color: "lightgray"
}
}
}
Button.qml
import QtQuick 2.0
Rectangle { width: 80; height: 50; color: "red"; anchors.fill: parent}
(Code reference for questions in comments section)
Main.py
import os
import random
import sys
from pathlib import Path
from PySide6.QtCore import Property, QUrl, QObject, Qt
from PySide6.QtGui import QColor, QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView
ColorRole = Qt.UserRole
BorderRole = Qt.UserRole
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStandardItemModel()
self._model.setItemRoleNames({Qt.DisplayRole: b"display", ColorRole: b"custom", BorderRole: b"custom2"})
#Property(QObject, constant=True)
def model(self):
return self._model
def add_button(self, text, color, bColor):
item = QStandardItem(text)
item.setData(color, ColorRole)
item.setData(bColor, BorderRole)
self._model.appendRow(item)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
manager = Manager()
view = QQuickView()
view.rootContext().setContextProperty("manager", manager)
view.setResizeMode(QQuickView.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / "view.qml")
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Error:
sys.exit(-1)
for i in range(6):
color = QColor(*random.sample(range(0, 255), 3))
border = QColor(*random.sample(range(0, 255), 3))
manager.add_button(f"button-{i}", color, border)
view.show()
res = app.exec()
sys.exit(res)
View.qml
import QtQuick 2.0
import QtQuick.Layouts 1.12
Item {
ColumnLayout {
id: colLayout
anchors.fill: parent
Repeater{
model: manager.model
Button{
color: model.custom
text: model.display
border.color: model.custom2
}
}
}
}
Button.qml
import QtQuick 2.0
Rectangle {
id: root
property alias text: txt.text
width: 80
height: 50
color: "red"
border.color: "black"
Text{
id: txt
anchors.centerIn: parent
}
}
The idea is that Python (or C++) provide the information to QML to create the items for example using a model and a Repeater.
On the other hand, if an item is going to be a child of a ColumnLayout then it should not use anchors since there will be conflicts since they both handle the geometry of the item.
Considering the above I have added more elements such as variable text, variable color, etc. to demonstrate the logic.
import os
import random
import sys
from pathlib import Path
from PySide6.QtCore import Property, QUrl, QObject, Qt
from PySide6.QtGui import QColor, QGuiApplication, QStandardItem, QStandardItemModel
from PySide6.QtQuick import QQuickView
ColorRole = Qt.UserRole
class Manager(QObject):
def __init__(self, parent=None):
super().__init__(parent)
self._model = QStandardItemModel()
self._model.setItemRoleNames({Qt.DisplayRole: b"display", ColorRole: b"custom"})
#Property(QObject, constant=True)
def model(self):
return self._model
def add_button(self, text, color):
item = QStandardItem(text)
item.setData(color, ColorRole)
self._model.appendRow(item)
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
manager = Manager()
view = QQuickView()
view.rootContext().setContextProperty("manager", manager)
view.setResizeMode(QQuickView.SizeRootObjectToView)
qml_file = os.fspath(Path(__file__).resolve().parent / "view.qml")
view.setSource(QUrl.fromLocalFile(qml_file))
if view.status() == QQuickView.Error:
sys.exit(-1)
for i in range(6):
color = QColor(*random.sample(range(0, 255), 3))
manager.add_button(f"button-{i}", color)
view.show()
res = app.exec()
sys.exit(res)
import QtQuick 2.0
import QtQuick.Layouts 1.12
Item {
ColumnLayout {
id: colLayout
anchors.fill: parent
Repeater{
model: manager.model
Button{
color: model.custom
text: model.display
}
}
}
}
import QtQuick 2.0
Rectangle {
id: root
property alias text: txt.text
width: 80
height: 50
color: "red"
Text{
id: txt
anchors.centerIn: parent
}
}
Update:
Each role must have a different numerical value since otherwise Qt cannot identify it, in your case you could change:
BorderRole = Qt.UserRole + 1

How can I send parent to a new ApplicationWindow

I want to recover the position and the size of the master "main.qml". But I do not know how to declare the parent of the new window.
I have no problem if I open the window directly from the window main.qml in javascript but through python I do not see how.
I think I have to use "self.win" but how declare it ?
Thanks for yours responses.
test.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
import sys
class Main2(QObject):
def __init__(self, engine, what_send_for_send_the_parent):
QObject.__init__(self)
"""
How can I say to the new windows who is the parent ?
"""
context = engine.rootContext()
context.setContextProperty("py_Page2", self)
engine.load('test2.qml')
self.win = engine.rootObjects()[0]
class Main(QObject):
def __init__(self, engine):
QObject.__init__(self)
self.context = engine.rootContext()
self.property = self.context.setContextProperty("py_Page", self)
self.load = engine.load('test.qml')
self.win = engine.rootObjects()[0]
print("Context", self.context) # <PyQt5.QtQml.QQmlContext object at 0xb65e6f30>
print("Property", self.property)# None
print("Load", self.property) # None
print("Win", self.win) # <PyQt5.QtGui.QWindow object at 0xb65e6f80>
if __name__ == "__main__":
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
main = Main(engine)
main2 = Main2(engine, "???")
engine.quit.connect(app.quit)
sys.exit(app.exec_())
test.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
id: "main"
visible: true
width: 200; height: 240;
Text {text: qsTr("main")}
}
test2.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
id: "main2"
visible: true
width: 200; height: 240;
x: main.x
y: main.y
Text {text: qsTr("main2")}
}
I think I have found :
class Main2(QObject):
def __init__(self, engine, master):
QObject.__init__(self)
context = engine.rootContext()
context.setContextProperty("main_x", master.win.property("x"))
context.setContextProperty("main_y", master.win.property("y"))
engine.load('test2.qml')
...
main = Main(engine)
main2 = Main2(engine, main)
...
And in the file qml
ApplicationWindow {
id: "main2"
visible: true
width: 200; height: 240;
x: main_x + 20
y: main_y + 120
Text {text: qsTr("main2")}
}
I can recover the value like that. Is this correct? Is there a more conventional way?
Although the solution works in this case it can fail in more real cases where each QML can load many components since the QML load is asynchronous but your procedure is synchronous.
The solution is to create a QObject and export it to QML using setContextProperty() so that it is accessible from all the QMLs that are loaded through the QQmlApplicationEngine. That QObject must have a property that is a mirror of the property you want to obtain.
main.py
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QPoint, QUrl
from PyQt5.QtGui import QGuiApplication
from PyQt5.QtQml import QQmlApplicationEngine
class Manager(QObject):
positionChanged = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self._position = QPoint()
#pyqtProperty(QPoint, notify=positionChanged)
def position(self):
return self._position
#position.setter
def position(self, p):
if self._position != p:
self._position = p
self.positionChanged.emit()
if __name__ == "__main__":
import os
import sys
app = QGuiApplication(sys.argv)
engine = QQmlApplicationEngine()
manager = Manager()
engine.rootContext().setContextProperty("manager", manager)
current_dir = os.path.dirname(os.path.realpath(__file__))
engine.load(QUrl.fromLocalFile(os.path.join("test.qml")))
engine.load(QUrl.fromLocalFile(os.path.join("test2.qml")))
engine.quit.connect(app.quit)
sys.exit(app.exec_())
test.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
id: root
visible: true
width: 200
height: 240
Text {
text: qsTr("main")
}
Component.onCompleted: manager.position = Qt.point(root.x, root.y)
}
test2.qml
import QtQuick 2.5
import QtQuick.Controls 1.4
ApplicationWindow {
visible: true
width: 200
height: 240
x: manager.position.x
y: manager.position.x
Text {
text: qsTr("main2")
}
}

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_())

qml doesn't work with vispy

In the process of studying Qt and Qt, Quick encountered a very interesting problem. I wanted to add to my application a widget on which something would be rendered using an OpenGl. I found a small example using vispy and decided to try it. And then something very interesting is happening. The fact is that one of my widgets is written in QML, and when I launch my application, the widget with OpenGL worked. A black square appears instead of the QML-widget. Also in the log the following is written:
WARNING: QQuickWidget cannot be used as a native child widget.
Consider setting Qt::AA_DontCreateNativeWidgetSiblings
Here my code:
import QtQuick 2.7
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.0
Rectangle {
width: 200
height: 200
color: 'white'
Rectangle {
id: lef_rec
width: parent.width / 2
height: parent.height
color: "green"
}
Rectangle {
width: parent.width / 2
height: parent.height
anchors.left: lef_rec.right
color: "blue"
}
}
In Python:
self.qml_wdg = QQuickWidget()
self.qml_wdg.setSource(QtCore.QUrl("main.qml"))
canvas = Canvas(keys='interactive', vsync=False).native
layout = QtWidgets.QVBoxLayout()
layout.addWidget(canvas)
layout.addWidget(self.qml_wdg)
self.centralwidget.setLayout(layout)
Separately everything works, together there is this error. I'm wondering what this problem is?
You must place the attribute with setAttribute():
{your QApplication}.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings)
Complete code:
import sys
from PyQt5 import QtWidgets, QtCore, QtQuickWidgets
from vispy.app import Canvas
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent=parent)
self.centralwidget = QtWidgets.QWidget()
self.setCentralWidget(self.centralwidget)
self.qml_wdg = QtQuickWidgets.QQuickWidget()
self.qml_wdg.setSource(QtCore.QUrl("main.qml"))
canvas = Canvas(keys='interactive', vsync=False).native
layout = QtWidgets.QVBoxLayout()
layout.addWidget(canvas)
layout.addWidget(self.qml_wdg)
self.centralwidget.setLayout(layout)
app = QtWidgets.QApplication(sys.argv)
app.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Categories

Resources