QWidget not deleted with parent window - python

Using the below code, the __del__ method of my Preview widget never gets called. If I uncomment the "del window" line, it does. Why?
#!/usr/bin/env python
from PyQt4 import QtGui
class Preview(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
def __del__(self):
print("Deleting Preview")
class PreviewWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = Preview(self)
self.setCentralWidget(self.widget)
def __del__(self):
print("Deleting PreviewWindow")
if __name__ == "__main__":
app = QtGui.QApplication(["Dimension Preview"])
window = PreviewWindow()
window.show()
app.exec()
# del window

If a QObject subclass has a parent, then Qt will delete it when the parent is deleted. On the other hand, if a QObject subclass has no parent, it will (eventually) be deleted by python.
Hopefully this example will make things somewhat clearer:
from PyQt4 import QtGui
class Widget(QtGui.QWidget):
def __init__(self, parent):
QtGui.QWidget.__init__(self, parent)
self.destroyed.connect(self.handleDestroyed)
def __del__(self):
print ('__del__:', self)
def handleDestroyed(self, source):
print ('destroyed:', source)
class Foo(Widget):
def __init__(self, parent):
Widget.__init__(self, parent)
class Bar(Widget):
def __init__(self, parent):
Widget.__init__(self, parent)
class Window(Widget):
def __init__(self, parent=None):
Widget.__init__(self, parent)
self.foo = Foo(self)
self.bar = Bar(None)
if __name__ == "__main__":
app = QtGui.QApplication([__file__, '-widgetcount'])
window = Window()
window.show()
app.exec_()
Which outputs:
__del__: <__main__.Window object at 0x88f514c>
destroyed: <__main__.Foo object at 0x88f5194>
__del__: <__main__.Bar object at 0x88f51dc>
Widgets left: 0 Max widgets: 4
EDIT
On second thoughts, it appears that there may be a bug (or at least a difference in behaviour) with some versions of PyQt4.
As a possible workaround, it seems that creating two python names for the main widget and then explicitly deleting each of them may help to ensure that both the C++ and python sides of the object get destroyed.
If the following line is added to the above script:
tmp = window; del tmp, window
Then the output becomes:
__del__: <__main__.Window object at 0x8d3a14c>
__del__: <__main__.Foo object at 0x8d3a194>
__del__: <__main__.Bar object at 0x8d3a1dc>
Widgets left: 0 Max widgets: 4

Related

PyQt6: How can I fetch position of mouse pointer from QGraphicsScene and use the variables in the other class?

I'm new to pyqt6 and even to python. As the title indicates, I got stuck with handling mouse position variables. I was expecting to show coodinates of mouse pointer in the QLabel correspond to the mouse movement. I was able to fetch coordinates from mouseMoveEvent in the QGraphicsScene class to getPosition in the Window class. But, that coodinates couldn't be passed to the other function in Window class.
Here is my code so far.
import sys
from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QVBoxLayout, QWidget, QLabel
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
Window.getPosition(Window, event.scenePos().x())
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QWidget):
def __init__(self):
super().__init__()
self.Layout = QVBoxLayout()
self.gw = GraphicsView() # an image is meant to be set here.
self.Layout.addWidget(self.gw)
self.label = QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def getPosition(self, posX):
self.label.setText("Coordinate: x" + str(self.posX))
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())
and got an ERROR like this:
AttributeError: type object 'Window' has no attribute 'label'
It seemas the self in the getPosition function is set to GraphicsScene class after being called (unexpectedly for me). And I have no idea this approach works or not after reading several web documents and asking help for chatGPT. Possibly, I took wrong approach to layout of Widgets.
Any suggestion would be helpful because I'm stuck whole this week with dipression.
Thanks.
Using class Window as parameter to setPosition was wrong, one needs to use an instance of this class. I did this by climbing up the parent() methods and there may be prettier ways to achieve the same. However, it works for now and I did not want to throw in too many changes.
I marked notable changes with comments.
#!/usr/bin/env python3
import sys
from PyQt6 import QtWidgets
class GraphicsScene(QtWidgets.QGraphicsScene):
def __init__(self, parent=None):
super().__init__(parent)
def mouseMoveEvent(self, event):
self.posX = event.scenePos().x()
self.parent().parent().setPosition(event.scenePos().x()) # <-- crawl up the ancestry
class GraphicsView(QtWidgets.QGraphicsView):
def __init__(self, parent=None):
super(GraphicsView, self).__init__(parent)
self.setMouseTracking(True)
scene = GraphicsScene(self)
self.setScene(scene)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.Layout = QtWidgets.QVBoxLayout()
self.gw = GraphicsView(self) # <-- pass self here
self.Layout.addWidget(self.gw)
self.label = QtWidgets.QLabel("Coordinate: x") # wanna show coorinates here correspond to the mouse movement.
self.Layout.addWidget(self.label)
self.setLayout(self.Layout)
def setPosition(self, posX): # <-- this is a setter, not a getter
self.label.setText("Coordinate: x" + str(posX)) # <-- use argument posX
self.repaint()
print(posX)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec())

How to insert a Qwidget inside Qwidget

I am lost with all the parenting/initialising issues and have no idea why this does not work.
So I create a Label, then I create another Label with some painting in it, them I make a widget that contains the two, then I would like to put this new widget inside the main window... but nothing appears
import sys
import os
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Labhtml(QLabel):
def __init__(self):
super().__init__()
label = QLabel('html')
class Bar(QLabel):
def __init__(self):
super().__init__()
self.resize(100, 5)
def paintEvent(self, e):
qp = QPainter(self)
qp.setBrush(QColor(200, 0, 0))
qp.drawRect(0,0,200,3)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
widget = QWidget()
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(widget)
self.layout.addWidget(html)
self.layout.addWidget(bar)
class Example(QScrollArea):
def __init__(self):
super().__init__()
widget = QWidget()
layout = QVBoxLayout(widget)
layout.addWidget(Wid(widget))
self.setWidget(widget)
self.setWidgetResizable(True)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
First for the class Labhtml, when you inherit from QLabel, you can use the methods and the attributes of the base class, or use the instantiation mechanism to pass some parameters :
class Labhtml(QLabel):
def __init__(self):
super().__init__()
self.setText('html')
Then you don't need to create another widget inside the Wid class, but you have to refer to self instead :
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml()
bar = Bar()
self.layout = QVBoxLayout(self)
self.layout.addWidget(html)
self.layout.addWidget(bar)
About the instantiation mechanism you could also write the classes by declaring a new text argument (the same used for the Qlabel), and pass it when you create your instance :
class Labhtml(QLabel):
def __init__(self, text):
super().__init__(text)
class Wid(QWidget):
def __init__(self, parent):
super().__init__(parent=parent)
html = Labhtml('html')

Which one is a parent of child class ChildWidget (PySide)?

Can you help me and explain why print(str(self.parent())) returns MainWindow and self.print_base() returns QWidget? Where is parent() method defined? In super(ChildWidget, self).__init__(parent) parent goes to MainWindow init or in QWidget init?
import sys
from PySide import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.do_something() #sanity check
self.cw = ChildWidget(self)
self.setCentralWidget(self.cw)
self.show()
def do_something(self):
print 'doing something!'
class ChildWidget(QtGui.QWidget):
def print_base(self):
for base in self.__class__.__bases__:
print base.__name__
def __init__(self, parent):
super(ChildWidget, self).__init__(parent)
print(str(self.parent()))
self.print_base()
self.button1 = QtGui.QPushButton()
self.button1.clicked.connect(self.do_something_else)
self.button2 = QtGui.QPushButton()
self.button2.clicked.connect(self.parent().do_something)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.button1)
self.layout.addWidget(self.button2)
self.setLayout(self.layout)
self.show()
def do_something_else(self):
print 'doing something else!'
You are dealing with two types of hierarchy: 1) widgets hierary; 2) python classes hiearchy. The method "print_base" is listing all the base classes in a python POV, while "parent" returns the widget instance where the child widget is attached to.

How to access variables from one class to another class in PyQt4?

I want to get a string from the main window to use in a window triggered with a click. I know how to do it by putting all statements into a single class, but now I'm trying to do the same thing with one class per window. Here is the code:
import sys
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
def initUI(self):
self.value = QtGui.QLineEdit('23')
self.button = QtGui.QPushButton('Open Dialog')
self.button.clicked.connect(self.openDialog)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.value)
vbox.addWidget(self.button)
self.setLayout(vbox)
def openDialog(self):
self.entry = self.value.text()
print(self.entry)
Dialog().exec_()
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
win = Window()
self.text = win.entry
self.label = QtGui.QLabel(self.text)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
def main():
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
But I'm getting the error "AttributeError: 'Window' object has no attribute 'entry'" and I don't know any other way to try fix it. Can someone help me with it?
Create an instance of Dialog in the openDialog method, so that you can access its attributes directly. That way, the two classes can operate more independently, and you won't need to access the Window class from within the Dialog class:
def openDialog(self):
dialog = Dialog(self)
dialog.label.setText(self.value.text())
dialog.exec_()
print(dialog.label.text())
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QtGui.QLabel(self)
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.label)
self.setLayout(hbox)
Here
win = Window()
self.text = win.entry
you declare a new window and accesing its entry field but on your window class
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super().__init__()
self.initUI()
the entry field is not constructed.
So
Either you want to create a new window, so you have to put the self.entry on the constructor
You want to use the existing window an access its entry after calling openDialog
Edit:
Maybe here
class Dialog(QtGui.QDialog):
def __init__(self, parent=Window):
super().__init__()
you are initializing the class wrong. The parent constructor should be called with parent=Window too. Then from inside the dialog you could reference the window by doing self.parent

Modify listbox from another class with pyqt

I'm doing a GUI for a database through PyQt and Python. The main window (class Window) has a listbox where I put all my data, for this example I put "The program is working". Furthermore, The other window (class AddWin) help me to add new costumers to the database, but I couldn't modify the listbox from the class Addwin. I have the following code in my program and I would like to clean the listbox from the class AddWin, can you help me? or what is my mistake in the following code?
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
#Listbox
self.lista = QtGui.QListWidget(self)
self.lista.move(155,221)
self.lista.resize(855,455)
self.lista.addItem("The program is working")
class AddWin(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
main = Window()
main.lista.clear()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Your mistake is that code doesn't instantiate AddWin anywhere, so lista.clear is never called.
Your can test it by changing
window = Window()
to
window = AddWin()
LAST EDITED 21 / 8 / 2014 12 : 42
If your want to shard QtGui.QListWidget from QtGui.QMainWindow to QtGui.QDialog, Your can use pass value by reference to QtGui.QDialog.
Assume your QtGui.QMainWindow must have QtGui.QDialog (Or AddWin);
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
#Listbox
self.lista = QtGui.QListWidget(self)
self.lista.move(155,221)
self.lista.resize(855,455)
self.lista.addItem("The program is working")
self.myAddWin = AddWin(self.lista, self) # <- Pass QListWidget to your class
class AddWin(QtGui.QDialog):
def __init__(self, refQListWidget, parent=None):
QtGui.QDialog.__init__(self, parent)
self.myRefQListWidget = refQListWidget
self.myRefQListWidget.clear()

Categories

Resources