I am trying to display a QGraphicsView inside a QGraphicsWidget, but I can't seem to get it to work.
This is what I want to achieve, where the QGraphicsWidgets are all added to a QGraphicsScene, and each widget has their own QGraphicsScene and QGraphicsView.
The large box represents the main view, and the smaller boxes represent widgets, each with their own view and scenes.
My current implementation doesn't work, due to the following error:
TypeError: QGraphicsLinearLayout.addItem(QGraphicsLayoutItem):
argument 1 has unexpected type 'QGraphicsView'
My implemetation in a subclass of the QGraphicsWidget:
self.scene = QtGui.QGraphicsScene(self)
self.view = QtGui.QGraphicsView(self.scene)
self.view.setRenderHint(QtGui.QPainter.Antialiasing)
self.setLayout(QtGui.QGraphicsLinearLayout(Qt.Vertical))
self.layout().addItem(self.view)
I've tried to implement this with a QGraphicsLayoutItem, but I couldn't find anywhere to stick the QGraphicsView in.
Any help would be appreciated.
The function QGraphicsLinearLayout.addItem expects a QGraphicsWidget parameter and you are passing a normal QWidget (QGraphicsView).
You can use the class QGraphicsProxyWidget which is specially designed for wrapping QWidget into QGraphicsWidget.
I do not have Qt + Python on hands but I think it should look as follows:
proxy = parentView.addWidget(nestedView) # QWidget -> QGraphicsWidget
self.layout().addItem(proxy)
Related
# code#1
import sys
from PyQt5.QtWidgets import QApplication, QPushButton
def onResize(event):
print("Nice to get here!")
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = QPushButton('Test')
widget.resize(640, 480)
widget.show()
widget.resizeEvent = onResize
sys.exit(app.exec_())
The new resizeEvent is never fired in this code#1(when I resize the window manually).
# code#2
import sys
from PyQt5.QtWidgets import QApplication, QPushButton
def onResize(event):
print("Nice to get here!")
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = QPushButton('Test')
widget.resize(640, 480)
widget.resizeEvent = onResize
widget.show()
sys.exit(app.exec_())
The new resizeEvent is nicely fired in this code#2(when I resize the window manually). And I can see msg printed out.
Does anyone know the reason? Even I add widget.update() and widget.show() after widget.resizeEvent = onResize in code#1, the resizeEvent code just keeps silence...
resizeEvent is a "virtual protected" method, and such methods should not be "overwritten" like this.
To implement them in a safe fashion, you'd better use subclassing:
class MyButton(QtWidgets.QPushButton):
def resizeEvent(self, event):
print("Nice to get here!")
Virtual methods are functions that are mostly used for subclasses.
When a subclass calls that method, it will look up in its class methods first.
If that method exists that method will be called, otherwise the MRO (as in Method Resolution Order) will be followed: simply speaking, "follow the steps back from the class to its inheritation(s) and stop when the method is found".
NOTE: in Python, almost everything is virtual, except things like "magic methods" ("dunder methods", the builtin methods that are enclosed within double underscores like __init__). With bindings like PyQt things are a bit more complex, and "monkey patching" (which completely relies on the virtual nature of Python) becomes less intuitive than it was.
In your case, the inheritance follows this path:
[python object]
Qt.QObject
QtWidget.QWidget
QtWidget.QAbstractButton
QtWidget.QPushButton
So, when you create QPushButton('Test') (calling its __init__(self, *args, **kwargs)), the path will be reversed:
__init__() a QtWidget.QPushButton with the new instance as first argument and the following "test" argument parameter;
QPushButton calls the inherited QAbstractButton class __init__(self, text), which gets the first argument after the instance as the button text;
QAbstractButton will call "some" of its methods or those of QWidget for size hints, font managing, etc.;
and so on...
In the same fashion, whenever yourButton.resizeEvent is called, the path will be reversed:
look for QtWidget.QPushButton.resizeEvent
look for QtWidget.QAbstractButton.resizeEvent
...
SIP (the tool created to generate python bindings for Qt) uses caching for virtuals, and as soon as a virtual [inherited] function is found, that function will always be called in the future if another Qt function requires it. This means that, as soon as no python override is found and the method lookup is successful, that method will always be used from that moment on, unless explicitly called (using "self.resizeEvent()" within your python code).
After calling show(), a QEvent.Resize is received by QWidget.event() (which is a virtual itself); if event() is not overwritten, the base class implementation is called, which will look up for the class resizeEvent function and call it with the event as argument.
Since, at that point, you've not overwritten it yet, PyQt will fallback to the default widget resizeEvent function, and from that point on it will always use that (according to the list above and QPushButton implementation, it will be the basic QWidget.resizeEvent call).
In your second example, show() is called after overwriting the resizeEvent function, allowing the event() function to "find" it (thus ignoring its base implementation along with those defined in the inherited classes) and will use yours until the program exits.
Also, in this case the method can be overwritten again, since SIP/Qt will not use caching anymore. This is a subtle but still very important difference to keep in mind: from this moment you can override that instance (note the bold characters) method as much as you want, as soon as you're certain it's not been called before!
def onResize1(event):
print("Nice to get here!")
def onResize2(event):
print("Can you see me?")
def changeResizeEvent(widget, func):
widget.resizeEvent = func
if __name__ == "__main__":
app = QApplication(sys.argv)
widget = QPushButton('Test')
widget.resize(640, 480)
# change the resize event function *before* it might be called
changeResizeEvent(widget, onResize1)
widget.show()
# change the function again after clicking
widget.clicked.connect(lambda: changeResizeEvent(widget, onResize2))
sys.exit(app.exec_())
As a rule of thumb, everything you see in the official Qt documentation that is labeled as virtual/protected generally requires a subclass to correctly override it. You can see that "[virtual protected]" text on the right of the resizeEvent() definition, and the function is also in the Protected Function list.
PS: with PyQt, some level of monkey patching works with classes too (meaning that you can overwrite class methods that will be automatically inherited by their subclasses), but that's not guaranteed and their behavior is often unexpected, especially due to the cross platform nature of Qt. It mainly depends on the class, its internal C++ behavior and inheritance(s), and how SIP relates to the original C++ objects.
Phil Thompson, the creator of PyQt talked about this in a thread.
PyQt caches lookups for Python reimplementations of methods. Monkey
patching will only work if you patch a method before it is looked up
for the first time. Your example will work for QWidget if you move the
call to show() after you patch it.
I think the best solution is to implement your own QPushButton subclass or assign it before show method as your code #2.
I'm little confusing meaning of widgets instance creation in PyQt.
Seems QWidget(self) can create a widget.
But what's the meaning of QWidget(instance name)?
For exmaple,
grid_layout = QGridLayout(self)
test_label = QWidget(grid_layout)
I know test_label created inherited from gridlayout.
But what's the meaning during program meaning?
Is that meaning test_label widget is under grid_layout?
For many of the widgets when the signature is just a single parameter like in both of your examples, the argument given sets the created widget/layouts parent.
So in your first example, using self as the argument means that the parent of the QGridLayout and the widget that the layout will be applied to is whatever widget self is.
In the second example you are setting the parent of QWidget to be the widget represented by it's argument. A QWidget with no parent becomes a new window, when set with a parent then the widget becomes a child window within the parent widget.
Neither one are inheriting anything. Inheritance only occurs during Subclassing.
I wanted to ask if we could just animate the fontsize of the QLabel in PyQt5 with QPropertyAnimation.
I tried by adding QPropertyAnimation(self.label , b"fontSize") but It was not working and I got the warning
I want you increase the text of the QLabel and then again switch to normal size
From the QPropertyAnimation description:
QPropertyAnimation interpolates over Qt properties. As property values are stored in QVariants, the class inherits QVariantAnimation, and supports animation of the same meta types as its super class.
A class declaring properties must be a QObject. To make it possible to animate a property, it must provide a setter (so that QPropertyAnimation can set the property's value).
All classes that inherit from QObject (including widgets) support Qt properties, and in fact most of them have "builtin" properties, but, obviously, not all properties support animations: they must be based on numeric values. You can animate a numeric change (eg: from 0 to 100), not a text (eg. from 'ABC' to 'PYZ').
For instance, all QWidget have a pos property, and since such property (which is a QPoint) is based on numbers, you can create an animation. You can see the list of supported variant types for animations in the QVariantAnimation docs. Consider that all documentation pages regarding QObject subclasses contain a property section (see the QWidget properties for example), and that properties obviously are inherited from their super classes.
There is no property for the font size, though, so there are two possibilities:
create a custom property (by using the pyqtProperty decorator in PyQt);
use a QVariantAnimation, which is not bound to any property;
Depending on the situation, one might prefer one approach or the other, but for this specific case it doesn't change that much; the QVariantAnimation is certainly simpler, the Qt property approach is more "compliant".
Here you can see how the property is created using the pyqtProperty decorator. Since the animation writes the property, having a setter is mandatory.
class AnimationTest(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.startButton = QtWidgets.QPushButton('Start')
self.label = QtWidgets.QLabel('Hello!')
self.labelFont = self.font()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.startButton)
layout.addWidget(self.label)
self.ani = QtCore.QPropertyAnimation(self, b'labelFontSize')
self.ani.setStartValue(10)
self.ani.setEndValue(80)
self.ani.setDuration(1500)
self.startButton.clicked.connect(self.ani.start)
#QtCore.pyqtProperty(int)
def labelFontSize(self):
return self.labelFont.pointSize()
#labelFontSize.setter
def labelFontSize(self, size):
self.labelFont.setPointSize(size)
self.label.setFont(self.labelFont)
import sys
app = QtWidgets.QApplication(sys.argv)
test = AnimationTest()
test.show()
sys.exit(app.exec_())
The variant animation is certainly shorter:
class AnimationTest(QtWidgets.QWidget):
def __init__(self):
# ...
self.ani = QtCore.QVariantAnimation()
self.ani.setStartValue(10)
self.ani.setEndValue(80)
self.ani.setDuration(1500)
self.ani.valueChanged.connect(self.updateLabelFont)
self.startButton.clicked.connect(self.ani.start)
def updateLabelFont(self, value):
self.labelFont.setPointSize(value)
self.label.setFont(self.labelFont)
Please consider that font size animations are often problematic, as most fonts have slightly different glyphs and spacings depending on the size. In the examples above I used a pretty long animation that will probably show the effect: while the size increases linearly, the change in the font usually is not very smooth.
I understand that QLabel attributes such as frameGeometry, pixmap and text can be recovered using their respective commands. But is it possible to get the value of "frame shadow" around each of these label widgets?
I have 3 Labels placed inside a Frame (inside a Window) using qt-designer. I assigned shadows for each of these labels by calling self.label_1.setFrameShadow(QFrame.Raised) or self.label_1.setFrameShadow(QFrame.Plain) within the QMainWindow class.
Now I wish to update their shadow attributes after checking to see if one of them is Raised or Plain. The error says that: 'QLabel' object has no attribute 'FrameShadow'. But why so if I was able to set it?
QLabel inherits from QFrame and therefore has an accessor frameShadow() for that property.
Unlike in other frameworks, Qt accessors don't start with get...
I want to draw my own object on a layout, so I'm trying to subclass gdk.drawable,
class link(gtk.gdk.Drawable):
def __init__(self,comp1,comp2,layout):
super(link, self).__init__()
self.x1=comp1.xpos
self.y1=comp1.ypos
self.x2=comp2.xpos
self.y2=comp2.ypos
self.layout=layout
error:
cannot create instance of abstract (non-instantiable) type `GdkDrawable'
I can do it without subclassing drawable using layout.bin_window.draw_line() in a method drawlink() of my link object, but I'm not able to create a custom graphic context gdk.gc for each object and I have to use the layout.get_style() which will be the same for all my links!
def drawlink(self):
gc = self.layout.get_style().fg_gc[gtk.STATE_NORMAL]
gc.line_style=gtk.gdk.LINE_ON_OFF_DASH
gc.line_width=6
self.layout.bin_window.draw_line(gc,self.x1, self.y1, self.x2, self.y2)
this is the reason I want to subclass drawable. if I can use a custom gc without subclassing drawable or(window, pixmap) it would be great.
Thanks
any alternative?
If I understand you correctly, what you want to do is not subclassing gtk.gdk.Drawable but populating a gtk.DrawingArea widget with your own content. The description of that widget is:
The gtk.DrawingArea widget is used for creating custom user interface elements. It's essentially a blank widget containing a gtk.gdk.Window that you can draw on.