qml doesn't work with vispy - python

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

Related

How show a glowing label in QMainWindow

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

PyQt5 QPushButton setSyleSheet does not change button color when pressed

After studying various examples in this forum, I tried to change the color of a button when pressed. The button is normally blue, and when it is pressed I want it to turn red. The following code does display a blue button with white text, but it does not change to red when pressed. Please advise. I'm fairly new to learning python/pyqt5.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QPushButton
class Push_button(QPushButton):
def __init__(self, parent=None):
super(Push_button, self).__init__(parent)
self.setStyleSheet("background-color: rgb(0,0,255); color: rgb(255,255,255); \
pressed {background-color : rgb(255,0,0); color: rgb(255,255,255);} ")
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.myButton = Push_button(self)
self.myButton.setText("myButton")
self.myButton.clicked.connect(self.myButtonClicked)
def myButtonClicked(self):
print("myButtonClicked")
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
You are not using selectors correctly.
Right now your stylesheet sets the blue background color universally, and the red color for classes named "pressed".
self.setStyleSheet('''
QPushButton {
background-color: rgb(0,0,255); color: rgb(255,255,255);
}
QPushButton:pressed {
background-color : rgb(255,0,0); color: rgb(255,255,255);
}
''')
Read more about selector types in the official documentation.

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

How to customise QGroupBox title in PyQt5?

Here's a piece of code that creates a simple QGroupBox:
from PyQt5.QtWidgets import (QApplication, QWidget,
QGroupBox, QGridLayout)
class QGroupBoxTest(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
gb = QGroupBox()
gb.setTitle('QGroupBox title')
appLayout = QGridLayout()
appLayout.addWidget(gb, 0, 0)
self.setLayout(appLayout)
self.setWindowTitle('QGroupBox test window')
self.setGeometry(300, 300, 300, 200)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
test = QGroupBoxTest()
test.show()
sys.exit(app.exec_())
and here's what the output looks like to me:
Now let's say I want to add some style to it and I do this by adding this line to the initUI method:
gb.setStyleSheet("border: 1px solid gray; border-radius: 5px")
here's the output:
As can be clearly seen from the pic, the title has ended up inside the frame and now is overlapping the frame border.
So I have three questions actually:
Why did the title move?
How do I move it about and place it wherever I want (if possible)?
What if I simply want to round off border corners without specifying the border-style property. Let's say I want the border-style to stay the same as in the first pic but with rounded corners. How do I do that?
1) Probably that's the default QT placement, in the first image the platform style is used, and its take care of borders and title placement, when you change the stylesheet you override something and you get the ugly placement.
2) You can control the "title" position using the QGroupBox:title controls, for example:
gb.setStyleSheet('QGroupBox:title {'
'subcontrol-origin: margin;'
'subcontrol-position: top center;'
'padding-left: 10px;'
'padding-right: 10px; }')
will result in something like this:
3) My suggestion is to create different strings for the stylesheet attributes you want to change, then compose them to create the style you want.
Even though this question has already been answered, I will post what I've figured out regarding technics of applying style sheets to widgets in PyQt which partly answers my original question. I hope someone will find it useful.
I think it's nice to keep the styles in separate css(qss) files:
/*css stylesheet file that contains all the style information*/
QGroupBox {
border: 1px solid black;
border-radius: 5px;
}
QGroupBox:title{
subcontrol-origin: margin;
subcontrol-position: top center;
padding: 0 3px 0 3px;
}
and the python code looks like this:
from PyQt5.QtWidgets import (QApplication, QWidget,
QGroupBox, QGridLayout)
from PyQt5.QtCore import QFile, QTextStream
class QGroupBoxTest(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
gb = QGroupBox()
gb.setTitle('QGroupBox title:')
gb.setStyleSheet(self.getStyleSheet("./styles.qss"))
appLayout = QGridLayout()
appLayout.addWidget(gb, 0, 0)
self.setLayout(appLayout)
self.setWindowTitle('QGroupBox test window')
self.setGeometry(300, 300, 300, 300)
def getStyleSheet(self, path):
f = QFile(path)
f.open(QFile.ReadOnly | QFile.Text)
stylesheet = QTextStream(f).readAll()
f.close()
return stylesheet
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
test = QGroupBoxTest()
test.show()
sys.exit(app.exec_())
which yields the following output:

PyQt: Getting the amount of lines from QTextEdit

I am trying to make a chat application, so i added the QTextEdit widget where user types the content, maximum height of widget is 30 pixels, so basically one line.
self.msgtext = QtGui.QTextEdit(self)
self.msgtext.setObjectName('msgtext')
self.msgtext.setStyleSheet("#msgtext {background-color: black; color: yellow; font-size: 18pt; }")
self.msgtext.setMaximumSize(500, 30)
self.msgtextplain = self.msgtext.toPlainText()
I want to increase the maximum height of the widget whenever user get's one new line, But i don't know if there is any signal that can tell that i have reached new line.
Is there any signal, that can count the amount of lines from QTextEdit?
You can use blockCount() but don't forget to also resize whatever parent you are using for your QTextEdit. Here is an example:
import sys
from PyQt5 import QtGui, QtWidgets, QtCore
class Window(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
self.msgtext = QtWidgets.QTextEdit(self)
self.msgtext.setObjectName('msgtext')
self.msgtext.setStyleSheet("#msgtext {background-color: black; color: yellow; font-size: 18pt; }")
self.msgtext.resize(500, 30)
self.msgtextplain = self.msgtext.toPlainText()
self.msgtext.textChanged.connect(self.changed)
def changed(self):
lines = self.msgtext.document().blockCount()
self.msgtext.resize(500, 30*lines)
self.resize(500, 30*lines)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
It's PyQt5. To do this in PyQt4 change everything that is QtWidgets to QtGui (and check the import lines evidently; this should be enough but do tell if you have any difficulties). Here is the result:

Categories

Resources