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.
Related
This question already has answers here:
change label from other class in a different file
(2 answers)
Closed 3 years ago.
How to update a Widget from Window A in Window B?
I have a widget that i want to change in my second window, how would i proceed from here?
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
#MyWidget
...
def openSecond(self):
self.SW = SecondWindow(#MyWidget)
self.SW.show()
class SecondWindow(QMainWindow):
def __init__(self, #MyWidget):
super(SecondWindow, self).__init__()
#MyWidget.changed
#Update to parent
...
Pass the self from class A as Argument when creating an instance of the class B, then you can access the items from class A in class B with self.parent.
from PySide2.QtWidgets import QMainWindow, QPushButton, QApplication, QLabel
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.button = ''
btn = QPushButton('Open Second', self)
btn.move(10, 10)
btn.clicked.connect(self.openSecond)
self.resize(420, 450)
button = QPushButton(self)
button.move(100,100)
button.setText("current text")
self.button = button
def openSecond(self):
self.SW = SecondWindow(self.button, parent=self)
self.SW.show()
class SecondWindow(QMainWindow):
def __init__(self, button, parent=None):
super(SecondWindow, self).__init__()
self.parent = parent
lbl = QLabel('Second Window', self)
button.setText("updated text")
self.parent.buttons = button
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
MW = MainWindow()
MW.show()
sys.exit(app.exec_())
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')
I have a main Window derived form QMainWindow that may show different Widgets, depending on the task at hand.
I created a simplified example below:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class MainWindow(QMainWindow):
def __init__(self, parent=None):
'''
Constructor
'''
QMainWindow.__init__(self, parent)
self.central_widget = QStackedWidget()
self.setCentralWidget(self.central_widget)
self.start_screen = Start(self)
self.second_screen = Second(self)
self.central_widget.addWidget(self.start_screen)
self.central_widget.addWidget(self.second_screen)
self.central_widget.setCurrentWidget(self.start_screen)
class Start(QWidget):
def __init__(self, parent=None):
super(Start, self).__init__(parent)
layout = QHBoxLayout()
button = QPushButton(text=QString('Push me!'))
layout.addWidget(button)
self.setLayout(layout)
self.connect(button, SIGNAL("clicked()"), self.change_widget)
def change_widget(self):
self.parent().setCurrentWidget(
self.parent().parent().second_screen)
class Second(QWidget):
def __init__(self, parent=None):
super(Second, self).__init__(parent)
layout = QHBoxLayout()
button = QPushButton(text=QString('Back to Start!'))
layout.addWidget(button)
self.setLayout(layout)
self.connect(button, SIGNAL("clicked()"), self.change_widget)
def change_widget(self):
self.parent().setCurrentWidget(
self.parent().parent().start_screen)
app = QApplication(sys.argv)
myWindow = MainWindow(None)
myWindow.show()
app.exec_()
This toggles between two different central widgets, nevertheless I found the handling with parent() functions tedious, and I'd like to know if there is a better method to organise the widgets.
The setCurrentWidget method will probably be always accessible with one parent() statement, but if for any reasons the hierarchy-depth changes, is there a better method to access the QMainWindow than parent().parent()? Or would I just re-create the Widget every time a button is clicked (I assume, if this would be a widget with data-Input possibilities, these data would be lost, which is annoying).
I appreciate any suggestions for improvement.
You should be using Signals. This allows any parent hierarchy and the child widgets don't have to have any knowledge of how they're being used or what order they're being displayed in. The main widget handles it all.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sys
class MainWindow(QMainWindow):
def __init__(self, parent=None):
'''
Constructor
'''
QMainWindow.__init__(self, parent)
self.central_widget = QStackedWidget()
self.setCentralWidget(self.central_widget)
self.start_screen = Start(self)
self.second_screen = Second(self)
self.central_widget.addWidget(self.start_screen)
self.central_widget.addWidget(self.second_screen)
self.central_widget.setCurrentWidget(self.start_screen)
self.start_screen.clicked.connect(lambda: self.central_widget.setCurrentWidget(self.second_screen))
self.second_screen.clicked.connect(lambda: self.central_widget.setCurrentWidget(self.start_screen))
class Start(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super(Start, self).__init__(parent)
layout = QHBoxLayout()
button = QPushButton(text=QString('Push me!'))
layout.addWidget(button)
self.setLayout(layout)
button.clicked.connect(self.clicked.emit)
class Second(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super(Second, self).__init__(parent)
layout = QHBoxLayout()
button = QPushButton(text=QString('Back to Start!'))
layout.addWidget(button)
self.setLayout(layout)
button.clicked.connect(self.clicked.emit)
app = QApplication(sys.argv)
myWindow = MainWindow(None)
myWindow.show()
app.exec_()
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
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