2

I have a widget that I need to recreate. I have made a massively simplified example below. In the example I want to recreate the widget with new attributes. I know in this example I could use QtGui.QLabel.setText(), however I can't do the same thing in the real program.

Here is the example:

from PyQt4 import QtGui
import sys

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super().__init__()
        self.initAttributes()
        self.initLabel()
        self.initUI()

    def initAttributes(self):
        self.text = 'initial text'

    def initLabel(self):
        self.label = QtGui.QLabel()
        self.label.setText(self.text)

    def changeText(self):
        self.text = 'different text'
        self.initLabel()
        self.initUI()

    def initUI(self):
        button = QtGui.QPushButton()
        button.clicked.connect(self.changeText)
        grid = QtGui.QGridLayout()
        grid.addWidget(self.label)
        grid.addWidget(button)
        self.setLayout(grid)
        self.show()


app = QtGui.QApplication(sys.argv)
window = MainWindow()
app.exec_()

What I'm trying to do here is make a widget that has some initial attributes, in this example a label with text 'initial text'. As aforementioned, I am not trying to change the attributes, I am trying to recreate the object with new ones taken from attributes belonging to the window object.

The idea behind the button is that when pressed, it will change the window's 'self.text' attribute to something else, and will try to recreate the label, using the same method as before, initLabel.

The result should be that the window is reinitialized, and the recreated label will have the new text 'different text'. However, nothing happens.

Edit: The solution is that setLayout cannot be called twice for the same widget. When using the widget resetting technique in Schollii's linked answer, it works as it should. Below are the amended, working functions (rather than dumping the whole code again)

def changeText(self):
    self.text = 'different text'
    self.initLabel()
    self.delLayout()
    self.initUI()

def delLayout(self):
    QtGui.QWidget().setLayout(self.layout())
csey
  • 85
  • 1
  • 9
  • Can you show how you have tried to recreate it? What is the problem when you do this? – Oliver Apr 12 '14 at 16:12
  • If you looked at the code you would see that the function connected to the button calls the initLabel function, which should set self.label to a blank QLabel. If you execute the code and press the button, nothing happens – csey Apr 12 '14 at 19:53
  • The expected result should be that a label is created with the text 'different text' because that is the value of self.text when initLabel is called – csey Apr 12 '14 at 19:55
  • I see you are re-creating everything in the widget, is this a requirement or is the requirement to recreate the label but you recreate the rest so you can see the new label? – Oliver Apr 13 '14 at 02:36
  • Yes all the other widgets in the real program would need to be reinitialized completely – csey Apr 13 '14 at 11:30
  • I notice you haven't made any edits to your question and that it has downvotes; people can't un-downvote unless you edit your question. Consider addressing one or more issues raised in the one or more of the above comments, with a bit of luck it will get un-downvoted. – Oliver Apr 16 '14 at 11:14
  • Thanks again Schollii, I have edited the question clarifying what the code was trying to do, and also the working code after following your advice. – csey Apr 17 '14 at 20:31
  • OK great. Just a note to avoid duplication of info: since your answer does not go beyond accepted answer, there is no point in having it. If it did go beyond accepted answer, better put it as a comment to accepted answer, or if required code formatting, create your own answer (leave the other person's answer accepted since that is the one that allowed you to move forward). – Oliver Apr 17 '14 at 22:11
  • Does this still stand if the solution was in the linked post? – csey Apr 17 '14 at 22:39
  • Yes you can state in a comment to accepted answer what parts you used (such as technique from linked answer). – Oliver Apr 18 '14 at 03:22

2 Answers2

2

In your code, you are doing the equivalent of this, when you click the button:

def changeText(self):
    self.text = 'different text'
    self.label = QtGui.QLabel(self.text)
    self.initUI()

This creates new label, and re-inits UI (repeats signal connections, create widgets, set layout etc).

If you want to re-create the label instead of setting its text, you should not re-init UI. Rather, you should just remove the items you have recreated from the layout and re-add them. Example:

self.layout().removeWidget(self.label)
self.text = 'new text'
self.label = QtGui.QLabel(self.text)
self.layout().addWidget(self.label, 0, 0)

If you want to recreate the whole widget you should clear all its layout's items, or replace the layout with a new empty one as explained in this SO post replacing layout on a QWidget with another layout (you can't call setLayout() more than once but you can transfer layouts!).

Community
  • 1
  • 1
Oliver
  • 27,510
  • 9
  • 72
  • 103
  • If I use the method of deleting the layout as shown in the linked post, then the UI can be reinitialized and it works as it should, three_pinapple's answer is the reason it wasn't working – csey Apr 13 '14 at 11:32
  • @CharlieSeymour yes i agree but unfortunately it is incorrect about layout replacement. In any case hopefully some find my answer useful :) Welcome to SO! – Oliver Apr 13 '14 at 13:44
  • Looking at it in more detail I see that you were in fact right, I have corrected your post to be the answer! Thanks for the help! – csey Apr 16 '14 at 08:10
0

Your example fails because you cannot set the layout of a QMainWindow if it already has one. If you run the example code with a terminal visible, you see the error below printed when clicking the button.

QWidget::setLayout: Attempting to set QLayout "" on MainWindow "", which already has a layout

In theory, you should be able to remove the old layout, and add in a new one, but that appears difficult in PyQt. (Edit: see Schollii's answer for how to remove and re-add a layout)

I would suggest looking at whether you actually need to recreate the entire layout, or whether you just need to replace a widget within the layout. If you decide the latter, there are plenty of stackoverflow posts on deleting/replacing widgets in layouts.

three_pineapples
  • 11,579
  • 5
  • 38
  • 75