PyQt: Parenting a Widget

Thomas Jollans tjol at tjol.eu
Tue Sep 26 04:52:49 EDT 2017


On 2017-09-26 08:16, Veek M wrote:
> On Tuesday, September 26, 2017 at 11:18:54 AM UTC+5:30, Veek M wrote:
>> Summary: Could someone explain widget and dialog parenting - the text book is not making sense.
>> ######################
>> I'm trying to understand widget parenting, from the book: Rapid GUI Programming, pg 118, and thereabouts - he says:
>>
>> A. All PyQt classes that derive from QObjectand this includes all the widgets,
>> since QWidget is a QObject subclasscan have a “parent”.
>>
>> B. PyQt automatically repar-
>> ents the widgets that are laid out. So although we did not give our widgets a
>> parent of self (the Form instance),when we call setLayout() the layout manager
>> gives ownership of the widgets and of itself to the form,and takes ownership of
>> any nested layouts itself. This means that none of the widgets that are laid out
>> is a top-level window, and all of them have parents, which is what we want. So
>> when the form is deleted, all its child widgets and layouts will be deleted with
>> -----------------------------
>> 1. In A, does he mean, you are ALLOWED to set a parent on a widget ONLY because its Base Class is QObject? 
>>
>> With DockWidgets, you have to explicitly parent them - why?
>>
>> 2. If I create two widgets and wdget.show() them, and app.exec_() - which one becomes the main-window and which one is the memory leak? I have not used a layout manager so, one widget with no parent auto-becomes the main-window (as per B), which would result in a leak with the other?
>>
>> #!/usr/bin/python
>>
>> import sys, os, re
>>
>>
>> from PyQt4.QtCore import *
>> from PyQt4.QtGui import *
>>
>> app = QApplication(sys.argv)
>>
>> lbl = QLabel('<font size=11 color=red><b>Hello World</b></font>')
>> lbl.setWindowFlags(Qt.SplashScreen)
>> lbl.show()
>> txtBrw = QTextBrowser()
>> txtBrw.show()
>>
>> QTimer.singleShot(3000, app.quit)
>> app.exec_()
>>
>> 3. QObject --> QWidget --> QDialog --> Form --> Form_Layout_Manager --> Nested_Layout_Manager
>>
>> B, says that the layout manager parents the widgets under it and makes 'Form' the parent. If the Form Layout Manager is taking charge of the Nested Layout Manager, who is the parent of the widgets under the Nested Layout Mangaer? 
>>
>> 4. In the Chapter on 'Dialogs', I am trying to create a QMainWindow Style application with one label as the central widget and one button to invoke a dialog. It doesn't work and I get:
>>
>> QWidget::setLayout: Attempting to set QLayout "" on Parent "", which already has a layout
>>
>> I tried this link and it made no sense:
>> https://stackoverflow.com/questions/25450598/qlayout-attempting-to-add-qlayout-to-qwidget-which-already-has-a-layout
>>
>> How does parenting work in PyQt? What is autoparented, what needs to be explicitly parented and who is scrwing whom? Additionally, why is my button, hiding?
>>
>>
>> #!/usr/bin/python
>>
>> import sys, os, re
>>
>>
>> from PyQt4.QtCore import *
>> from PyQt4.QtGui import *
>>
>> app = QApplication(sys.argv)
>>
>> class Dialog(QDialog):
>>     def __init__(self, parent = None):
>>         super(Dialog, self).__init__(parent)
>>         
>>         self.lbl = QLabel('Width: ')
>>         self.spn = QSpinBox()
>>         self.spn.setRange(0, 100)
>>         self.lbl.setBuddy(self.spn)
>>         
>>         self.chk = QCheckBox('&Beveled Edges')
>>
>>         self.lbl_styl = QLabel('Style')
>>         self.lst = QComboBox()
>>         self.lst.addItems(['dashed', 'dotted', 'star'])
>>         self.lbl_styl.setBuddy(self.lst)
>>         
>>         self.ok_btn = QPushButton('&Ok')
>>         self.cncl_btn = QPushButton('&Cancel')
>>
>>         self.layout([(self.lbl, 0, 0), (self.spn, 0, 1), (self.chk, 0, 2), 
>>                      (self.lbl_styl, 1, 0), (self.lst, 1, 1), 
>>                      (self.ok_btn, 2, 0), (self.cncl_btn, 2, 1)])
>>
>>     def layout(self, wgts = []):
>>         self.lyt = QGridLayout()
>>         for wgt, row, col in wgts:
>>             self.lyt.addWidget(wgt, row, col)
>>             
>>         self.setLayout(self.lyt)
>>         
>>
>> class Parent(QMainWindow):
>>     def __init__(self, parent = None):
>>         super(Parent, self).__init__(parent)
>>         
>>         lbl = QLabel('HELLO WORLD')
>>         btn = QPushButton('&Start Dialog')
>>         lbl.setBuddy(btn)
>>         
>>         lyt = QHBoxLayout()
>>         lyt.addWidget(lbl)
>>         lyt.addWidget(btn)
>>         
>>         self.setLayout(lyt)
>>         self.connect(btn, SIGNAL('clicked()'), self.popup_dialog)
>>
>>     def popup_dialog(self):
>>         x = Dialog(self)
>>         if x.exec_():
>>             print(x.spn.value())
>>
>> p = Parent()    
>> p.show()
>>
>> app.exec_()
> 
> I fixed some of it by deleting the:
> Parent
>  self.setLayout(lyt) 
> 
> I don't understand why that works - aren't we supposed to attach a layout to the Parent/MainWindow object? Why not?
> 
> Also, I made the: 
> Parent
>  self.setCentralWidget(btn)

IIRC, QMainWindow is a bit weird, since it provides things like menu and
status bars. Everything normally goes into a "central widget".

The (or: a) thing to do is (I suspect):

class Parent(QMainWindow):
    def __init__(self, parent=None):
	# This is the future. super() is smart now.
        super().__init__(parent)

        lbl = QLabel('HELLO WORLD')
        btn = QPushButton('&Start Dialog')
        lbl.setBuddy(btn)

        lyt = QHBoxLayout()
        lyt.addWidget(lbl)
        lyt.addWidget(btn)

	central_widget = QWidget()
        central_widget.setLayout(lyt)
        self.setCentralWidget(central_widget)
	
	# Use new-style signals and slots! They're nicer!
	btn.clicked.connect(self.popup_dialog)

    def popup_dialog(self):
        x = Dialog(self)
        if x.exec_():
            print(x.spn.value())

I normally like to explicitly parent my widgets on construction, but
that's not necessary and I'm probably doing it wrong anyway.

Aside: I notice you're using PyQt4. That's great, I do too (for now),
but I suggest you consider moving to PyQt5. PyQt4 is slowly
disappearing. Anaconda (on Windows at least) has annoying dependency
structures that force you to use old versions of the scientific python
stack if you want to use qt4. I'm going to have to upgrade soon.

The differences between PyQt4 and PyQt5 aren't massive, but they've
moved around some classes (QtGui has been split into two packages, which
is going to be annoying to migrate) and support for old-style signal and
slot syntax has been dropped (good riddance).

Also, if you're using Python 2: don't. Just don't. It's not 2012 any
more. (I don't know what you're using, but /usr/bin/python is normally
Python 2)

> 
> so the button is the central widget. To use the Label, I think I would have to create a composite widget which is too complicated for me, currently.
> 
> Now. it works so I tested some more and added:
>     def popup_dialog(self):
>         x = Dialog(self)
>         y = Dialog(self)
>         y.show()
>         if x.exec_():
>             print(x.spn.value())
> AND
> 
> class Dialog(QDialog):
>     self.connect(self.cncl_btn, SIGNAL('clicked()'), self.close)
> 
> So my dialog pops up and because exec_ is being called on 'x', I have to first close that widget before I can interact with my button and 'y'. However, let's say I close the 'x' dialog - then I can click on 'Button' and I get two more dialogs which don't lock anything - why??
> 


-- 
Thomas Jollans



More information about the Python-list mailing list