Pyqt widget lingers after being removed - python

I have a QVBox layout that houses a QVBox layout and a QHBox layout. I use the other QVBox layout to hold dynamically created GUI objects and the QHBox layout to hold the buttons that add/remove those objects. Everything works correctly if I position the QHBox on top of the QVBox, but when I try to position the QHBox beneath the QVBox the objects aren't removed correctly but stay there "lingering" on top of the QHBox. I'll upload pictures to demonstrate the problem. First picture is before taking action, second is after creating a new object and third is after deleting the object
Here is the code that creates and deletes the new objects
def addClient(self):
if (len(self.clients) < 5):
client = clientComponent(self)
self.clients.append(client)
index = len(self.clients)-1
self.vLayout3.addWidget(self.clients[index])
client.setIndex(index)
self.clients[index].startButton.clicked.connect(partial(self.threadcontrol, '2', client.getIndex()))
self.clients[index].stopButton.clicked.connect(partial(self.clientstop, '0', client.getIndex()))
def deleteClient(self):
if (len(self.clients) > 1):
self.vLayout3.removeWidget(self.clients.pop())
This is where I complete the layout
def initializeUi(self):
self.mainWidget = QWidget(self)
self.setCentralWidget(self.mainWidget)
self.mainLayout = QVBoxLayout(self.mainWidget)
self.hLayout1 = QHBoxLayout()
self.hLayout2 = QHBoxLayout()
self.vLayout1 = QVBoxLayout()
self.vLayout2 = QVBoxLayout()
self.vLayout3 = QVBoxLayout()
self.addServer()
self.addClient()
self.serverBox = QGroupBox('Server')
self.clientBox = QGroupBox('Client')
self.traffic1 = QLabel('0.0Mb/s', self)
self.traffic1.setAlignment(Qt.AlignRight)
self.traffic2 = QLabel('0.0Mb/s', self)
self.traffic2.setAlignment(Qt.AlignCenter)
self.traffic3 = QLabel('0.0Mb/s', self)
self.traffic3.setAlignment(Qt.AlignLeft)
self.newClientButton = QPushButton('+', self)
self.deleteClientButton = QPushButton('-', self)
self.hLayout1.addWidget(self.traffic1)
self.hLayout1.addWidget(self.traffic2)
self.hLayout1.addWidget(self.traffic3)
self.hLayout2.addWidget(self.newClientButton)
self.hLayout2.addWidget(self.deleteClientButton)
self.vLayout2.addLayout(self.vLayout3)
self.vLayout2.addLayout(self.hLayout2)
self.mainLayout.addWidget(self.plot)
self.mainLayout.addLayout(self.hLayout1)
self.serverBox.setLayout(self.vLayout1)
self.mainLayout.addWidget(self.serverBox)
self.clientBox.setLayout(self.vLayout2)
self.mainLayout.addWidget(self.clientBox)

This is happening because your main window remains the parent of the client widgets after you remove them from the layout. You will see similar behaviour if you assign a widget a parent widget without adding it to any layout.
Removing the parent should resolve the issue.
def deleteClient(self):
if (len(self.clients) > 1):
client = self.clients.pop()
self.vLayout3.removeWidget(client)
client.setParent(None)
You may also need to make a call to adjustSize to resize the window to fit the remaining widgets

When you delete a widget from layout it still remains in parent widget's
object tree, so it gets displayed outside of any layout.
To remove a widget from the object tree call widget.setParent(None).
widget.deleteLater() also works.
Here is my MCVE(Qt4, Py2.7):
from PyQt4.QtGui import (QApplication, QWidget, QPushButton,
QVBoxLayout, QHBoxLayout)
app=QApplication([])
self = QWidget()
main_layout = QVBoxLayout(self)
clients = []
l2 = QHBoxLayout()
main_layout.addLayout(l2)
b_add = QPushButton('add', self)
l2.addWidget(b_add)
def addClient():
b = QPushButton(str(len(clients)), self)
clients.append(b)
main_layout.addWidget(b)
b_add.clicked.connect(addClient)
b_rm = QPushButton('rm', self)
l2.addWidget(b_rm)
def deleteClient():
b = clients.pop()
main_layout.removeWidget(b)
# comment out two following lines to get the behavior you observe
b.setParent(None)
self.adjustSize()
b_rm.clicked.connect(deleteClient)
self.show()
app.exec_()
On my system I also have to call self.adjustSize() after deletion to resize the main window

Related

Change in PyQt QDialog Layout Does Not Update Correctly -- bug or my error? [duplicate]

I have a QVBox layout that houses a QVBox layout and a QHBox layout. I use the other QVBox layout to hold dynamically created GUI objects and the QHBox layout to hold the buttons that add/remove those objects. Everything works correctly if I position the QHBox on top of the QVBox, but when I try to position the QHBox beneath the QVBox the objects aren't removed correctly but stay there "lingering" on top of the QHBox. I'll upload pictures to demonstrate the problem. First picture is before taking action, second is after creating a new object and third is after deleting the object
Here is the code that creates and deletes the new objects
def addClient(self):
if (len(self.clients) < 5):
client = clientComponent(self)
self.clients.append(client)
index = len(self.clients)-1
self.vLayout3.addWidget(self.clients[index])
client.setIndex(index)
self.clients[index].startButton.clicked.connect(partial(self.threadcontrol, '2', client.getIndex()))
self.clients[index].stopButton.clicked.connect(partial(self.clientstop, '0', client.getIndex()))
def deleteClient(self):
if (len(self.clients) > 1):
self.vLayout3.removeWidget(self.clients.pop())
This is where I complete the layout
def initializeUi(self):
self.mainWidget = QWidget(self)
self.setCentralWidget(self.mainWidget)
self.mainLayout = QVBoxLayout(self.mainWidget)
self.hLayout1 = QHBoxLayout()
self.hLayout2 = QHBoxLayout()
self.vLayout1 = QVBoxLayout()
self.vLayout2 = QVBoxLayout()
self.vLayout3 = QVBoxLayout()
self.addServer()
self.addClient()
self.serverBox = QGroupBox('Server')
self.clientBox = QGroupBox('Client')
self.traffic1 = QLabel('0.0Mb/s', self)
self.traffic1.setAlignment(Qt.AlignRight)
self.traffic2 = QLabel('0.0Mb/s', self)
self.traffic2.setAlignment(Qt.AlignCenter)
self.traffic3 = QLabel('0.0Mb/s', self)
self.traffic3.setAlignment(Qt.AlignLeft)
self.newClientButton = QPushButton('+', self)
self.deleteClientButton = QPushButton('-', self)
self.hLayout1.addWidget(self.traffic1)
self.hLayout1.addWidget(self.traffic2)
self.hLayout1.addWidget(self.traffic3)
self.hLayout2.addWidget(self.newClientButton)
self.hLayout2.addWidget(self.deleteClientButton)
self.vLayout2.addLayout(self.vLayout3)
self.vLayout2.addLayout(self.hLayout2)
self.mainLayout.addWidget(self.plot)
self.mainLayout.addLayout(self.hLayout1)
self.serverBox.setLayout(self.vLayout1)
self.mainLayout.addWidget(self.serverBox)
self.clientBox.setLayout(self.vLayout2)
self.mainLayout.addWidget(self.clientBox)
This is happening because your main window remains the parent of the client widgets after you remove them from the layout. You will see similar behaviour if you assign a widget a parent widget without adding it to any layout.
Removing the parent should resolve the issue.
def deleteClient(self):
if (len(self.clients) > 1):
client = self.clients.pop()
self.vLayout3.removeWidget(client)
client.setParent(None)
You may also need to make a call to adjustSize to resize the window to fit the remaining widgets
When you delete a widget from layout it still remains in parent widget's
object tree, so it gets displayed outside of any layout.
To remove a widget from the object tree call widget.setParent(None).
widget.deleteLater() also works.
Here is my MCVE(Qt4, Py2.7):
from PyQt4.QtGui import (QApplication, QWidget, QPushButton,
QVBoxLayout, QHBoxLayout)
app=QApplication([])
self = QWidget()
main_layout = QVBoxLayout(self)
clients = []
l2 = QHBoxLayout()
main_layout.addLayout(l2)
b_add = QPushButton('add', self)
l2.addWidget(b_add)
def addClient():
b = QPushButton(str(len(clients)), self)
clients.append(b)
main_layout.addWidget(b)
b_add.clicked.connect(addClient)
b_rm = QPushButton('rm', self)
l2.addWidget(b_rm)
def deleteClient():
b = clients.pop()
main_layout.removeWidget(b)
# comment out two following lines to get the behavior you observe
b.setParent(None)
self.adjustSize()
b_rm.clicked.connect(deleteClient)
self.show()
app.exec_()
On my system I also have to call self.adjustSize() after deletion to resize the main window

What is the difference between QLayout's setAlignment(QWidget, Alignment), setAlginment(QLayout, Alignment) and setAlginment(Alignment)? (PyQt5)

In the following code:
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QTableWidget, QPushButton
class Window(QtWidgets.QMainWindow):
def __init__(self, method):
super(Window, self).__init__()
mainWidget = QWidget()
mainLayout = QHBoxLayout(mainWidget)
table = QTableWidget(10, 3)
button1 = QPushButton("Play")
button2 = QPushButton("Cancel")
mainLayout.addWidget(table)
mainLayout.addWidget(button1)
mainLayout.addWidget(button2)
if (method == 1):
rtnValue = mainLayout.setAlignment(button1, Qt.AlignTop)
print("Method 1:", rtnValue)
elif (method == 2):
rtnValue = mainLayout.setAlignment(mainLayout, Qt.AlignTop)
print("Method 2:", rtnValue)
else:
rtnValue = mainLayout.setAlignment(Qt.AlignTop)
print("Method X:", rtnValue)
self.setCentralWidget(mainWidget)
self.show()
if __name__ == "__main__":
print("python QLayoutAlignment.py[ <MethodToUse=1>")
app = QApplication(sys.argv)
method = 1 if (len(sys.argv) < 2) else int(sys.argv[1])
GUI = Window(method)
sys.exit(app.exec_())
when I call the program like below with mainLayout.setAlignment(button1, Qt.AlignTop) being called, it works as expected with the "Play" button aligned at the top and "Cancel" button aligned at the center vertically. I also found the documentation for bool QLayout::setAlignment(QWidget *w, Qt::Alignment alignment) although in Qt.
python QLayoutAlignment.py 1
However when I call the the program like below with mainLayout.setAlignment(mainLayout, Qt.AlignTop) being called, it does not seem to work. All the buttons are vertically center aligned. I interpreted the Qt documentation of bool QLayout::setAlignment(QLayout *l, Qt::Alignment alignment)) as "it align all the added widget of the layout to the set alignment". So what does this function actually do (when is it used)?
python QLayoutAlignment.py 2
Lastly, I also saw another example from Center and top align a QVBoxLayout. When I call the program like below with mainLayout.setAlignment(Qt.AlignTop) being called, it also does not work with all the buttons vertically center aligned. For this one I could not find its documentation. So what does this function actually do (when is it used) and where can I find its documentation?
python QLayoutAlignment.py 3
The .setAlignment method which accepts a layout is used for aligning sub-layouts, that is child layouts you've added to the parent layout using .addLayout.
Below is a little demo based on your code.
import sys
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QTableWidget, QPushButton
class Window(QtWidgets.QMainWindow):
def __init__(self, method=0):
super(Window, self).__init__()
mainWidget = QWidget()
self.mainLayout = QHBoxLayout(mainWidget)
table = QTableWidget(10, 3)
self.button1 = QPushButton("Play")
self.button2 = QPushButton("Cancel")
self.subLayout = QHBoxLayout()
buttona1 = QPushButton("1")
buttona1.clicked.connect(self.clicked1)
buttona2 = QPushButton("2")
buttona2.clicked.connect(self.clicked2)
buttona3 = QPushButton("3")
buttona3.clicked.connect(self.clicked3)
buttona4 = QPushButton("4")
buttona4.clicked.connect(self.clicked4)
self.subLayout.addWidget(buttona1)
self.subLayout.addWidget(buttona2)
self.subLayout.addWidget(buttona3)
self.subLayout.addWidget(buttona4)
self.mainLayout.addWidget(table)
self.mainLayout.addWidget(self.button1)
self.mainLayout.addWidget(self.button2)
self.mainLayout.addLayout(self.subLayout)
self.setCentralWidget(mainWidget)
self.show()
def clicked1(self):
rtnValue = self.mainLayout.setAlignment(self.button1, Qt.AlignTop)
print("Method 1:", rtnValue)
def clicked2(self):
rtnValue = self.mainLayout.setAlignment(self.mainLayout, Qt.AlignTop)
print("Method 2:", rtnValue)
def clicked3(self):
rtnValue = self.mainLayout.setAlignment(Qt.AlignTop)
print("Method 3:", rtnValue)
def clicked4(self):
rtnValue = self.mainLayout.setAlignment(self.subLayout, Qt.AlignTop)
print("Method 4:", rtnValue)
if __name__ == "__main__":
print("python QLayoutAlignment.py[ <MethodToUse=1>")
app = QApplication(sys.argv)
method = 1 if (len(sys.argv) < 2) else int(sys.argv[1])
GUI = Window(method)
sys.exit(app.exec_())
You'll notice if you trigger this self.mainLayout.setAlignment(self.mainLayout, Qt.AlignTop) the return value is False. This is telling you that the layout you're aligning could not be found in the current layout. Since you're calling .setAlignment on mainLayout the layout you're affecting must be in that layout.
In the 4th method, I've added a sub-layout, and as you can see this ( rtnValue = self.mainLayout.setAlignment(self.subLayout, Qt.AlignTop)) works as expected and returns True.
First of all, it's important to understand that the Qt layout system works by using layout items (see QLayoutItem), which are abstract items that are used as virtual containers for objects: widgets, spacers and layouts (when nested layouts are used). Every QLayout is, in fact, a subclass of QLayoutItem.
Using setAlignment means setting the alignment of the specified layout item:
layout.setAlignment(item, alignment) sets the alignment of item, which has to be directly contained in layout;
layout.setAlignment(alignment) sets the alignment of layout related to its parent layout; note that this does not mean that items inside layout will use the specified alignment;
Your second case, mainLayout.setAlignment(mainLayout, Qt.AlignTop), does not work and returns False because mainLayout is, obviously, not "contained" in mainLayout. In fact, if you carefully read the documentation, it says:
returns true if w/l is found in this layout (not including child layouts);
In your third case, you don't see any result because mainLayout is the top layout for the widget, and since there's no parent layout the alignment seems to be ignored. As specified above, using layout.setAlignment(alignment) does not set the alignment of child items, but only of the layout item of layout. If you add that mainLayout to another layout, you will see that the alignment is respected for the layout.
Setting the layout alignment is rarely used, also because it's often counterintuitive: one might led to believe that setting the alignment of a layout will align its contents, but that's not the case.
To clarify, consider that setting the layout alignment is almost the same as creating a widget with that layout, and adding that widget with the specified alignment. With that in mind, it makes more sense: you're not aligning the contents of the widget, but the widget itself.
Consider the following example: besides the table on the left (used for comparison), I'm adding a layout on the left by specifying its alignment, then I'm adding a widget on the right by specifying the alignment of the widget for the main layout. As you can see, they appear exactly the same.
from PyQt5 import QtCore, QtWidgets
import sys
app = QtWidgets.QApplication(sys.argv)
test = QtWidgets.QWidget()
mainLayout = QtWidgets.QHBoxLayout(test)
# a very tall table to show the difference in alignment
mainLayout.addWidget(QtWidgets.QTableView(minimumHeight=300))
leftLayout = QtWidgets.QHBoxLayout()
# setting the alignment of leftLayout relative to mainLayout
leftLayout.setAlignment(QtCore.Qt.AlignTop)
leftLayout.addWidget(QtWidgets.QTableView(maximumHeight=100))
leftLayout.addWidget(QtWidgets.QPushButton())
mainLayout.addLayout(leftLayout)
rightWidget = QtWidgets.QWidget()
# adding the widget to mainLayout by aligning it on top as with leftLayout
mainLayout.addWidget(rightWidget, alignment=QtCore.Qt.AlignTop)
rightLayout = QtWidgets.QHBoxLayout(rightWidget)
rightLayout.addWidget(QtWidgets.QTableView(maximumHeight=100))
rightLayout.addWidget(QtWidgets.QPushButton())
test.show()
sys.exit(app.exec_())
Finally, if you want to align widgets, you either specify the alignment for each widget, or you add nested layout.
When many widgets are going to be added with the same alignment, the nested layout is usually the best solution: in your case, add a vertical layout to the main layout, then add horizontal layout to the vertical for the buttons, and add a stretch to the vertical to "push" the horizontal layout on top.
Alternatively, you can use a grid layout and eventually use spacers to ensure that the widgets are "aligned" as required.
from PyQt5 import QtCore, QtWidgets
import sys
app = QtWidgets.QApplication(sys.argv)
test = QtWidgets.QWidget()
mainLayout = QtWidgets.QHBoxLayout(test)
class Button(QtWidgets.QPushButton):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setSizePolicy(
QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Preferred
)
noAlignGroup = QtWidgets.QGroupBox('no alignment')
mainLayout.addWidget(noAlignGroup)
noAlignLayout = QtWidgets.QHBoxLayout(noAlignGroup)
noAlignLayout.addWidget(QtWidgets.QTableView())
noAlignLayout.addWidget(Button())
noAlignLayout.addWidget(Button())
widgetAlignGroup = QtWidgets.QGroupBox('addWidget(widget, alignment)')
mainLayout.addWidget(widgetAlignGroup)
widgetAlignLayout = QtWidgets.QHBoxLayout(widgetAlignGroup)
widgetAlignLayout.addWidget(QtWidgets.QTableView())
widgetAlignLayout.addWidget(Button(), alignment=QtCore.Qt.AlignTop)
widgetAlignLayout.addWidget(Button(), alignment=QtCore.Qt.AlignTop)
layoutAlignGroup = QtWidgets.QGroupBox('nestedLayout.setAlignment()')
mainLayout.addWidget(layoutAlignGroup)
layoutAlignLayout = QtWidgets.QHBoxLayout(layoutAlignGroup)
layoutAlignLayout.addWidget(QtWidgets.QTableView())
buttonLayout = QtWidgets.QHBoxLayout()
layoutAlignLayout.addLayout(buttonLayout)
buttonLayout.setAlignment(QtCore.Qt.AlignTop)
buttonLayout.addWidget(Button())
buttonLayout.addWidget(Button())
stretchGroup = QtWidgets.QGroupBox('nestedLayout + stretch')
mainLayout.addWidget(stretchGroup)
stretchLayout = QtWidgets.QHBoxLayout(stretchGroup)
stretchLayout.addWidget(QtWidgets.QTableView())
rightLayout = QtWidgets.QVBoxLayout()
stretchLayout.addLayout(rightLayout)
buttonLayout = QtWidgets.QHBoxLayout()
rightLayout.addLayout(buttonLayout)
buttonLayout.addWidget(Button())
buttonLayout.addWidget(Button())
rightLayout.addStretch()
gridAlignGroup = QtWidgets.QGroupBox('grid + spacer')
mainLayout.addWidget(gridAlignGroup)
gridLayout = QtWidgets.QGridLayout(gridAlignGroup)
gridLayout.addWidget(QtWidgets.QTableView(), 0, 0, 2, 1)
gridLayout.addWidget(Button(), 0, 1)
gridLayout.addWidget(Button(), 0, 2)
spacer = QtWidgets.QSpacerItem(1, 50, vPolicy=QtWidgets.QSizePolicy.Expanding)
gridLayout.addItem(spacer, 1, 1)
test.show()
sys.exit(app.exec_())

The layout is incorrect after remove widget

I am implement my project using pyqt5. Currently, I have a window including many widget. Now, I want to remove some widgets. The window looks like:
Now, I want to remove the 'name1' widget including the QLabel and QPushButton.
However, after removing all 'name1' widgets, the 'name2' widgets including QLabel and QPushButton can not self-adapte with the window, like:
All my code is:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subText = QLabel(name)
subText.setObjectName(name)
subBtn = QPushButton(name)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
def remove(self, checked=False):
name = self.__removeText.text()
while True:
child = self.__ROIsLayout.takeAt(0)
if child == None:
break
while True:
subChild = child.takeAt(0)
if subChild == None:
break
obName = subChild.widget().objectName()
if name == obName:
widget = subChild.widget()
widget.setParent(None)
child.removeWidget(widget)
self.__ROIsLayout.removeWidget(widget)
del widget
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
update:
Actually, the issue may be the takeAt. The following code is workable:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys
class Window(QDialog):
def __init__(self):
super().__init__()
self.initGUI()
self.show()
def initGUI(self):
layout = QVBoxLayout()
self.setLayout(layout)
removeLayout = QHBoxLayout()
self.__removeText = QLineEdit()
self.__removeBtn = QPushButton('Remove')
self.__removeBtn.clicked.connect(self.remove)
removeLayout.addWidget(self.__removeText)
removeLayout.addWidget(self.__removeBtn)
ROIsLayout = QVBoxLayout()
for name in ['name1', 'name2']:
subLayout = QHBoxLayout()
subLayout.setObjectName(name)
subText = QLabel(name, parent=self)
subText.setObjectName(name)
subBtn = QPushButton(name, parent=self)
subBtn.setObjectName(name)
subLayout.addWidget(subText)
subLayout.addWidget(subBtn)
ROIsLayout.addLayout(subLayout)
print(name, subLayout, subText, subBtn)
layout.addLayout(removeLayout)
layout.addLayout(ROIsLayout)
self.__ROIsLayout = ROIsLayout
self.record = [subLayout, subText, subBtn]
def remove(self, checked=False):
layout = self.record[0]
txt = self.record[1]
btn = self.record[2]
layout.removeWidget(txt)
txt.setParent(None)
txt.deleteLater()
layout.removeWidget(btn)
btn.setParent(None)
btn.deleteLater()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
sys.exit(app.exec_())
But, I have printed the QLabel/QPushButton in the self.record, and I find it is the same with that from child.takeAt(0).widget().
The main issue in your code is that you're constantly using takeAt(). The result is that all items in the __ROIsLayout layout will be removed from it (but not deleted), which, in your case, are the sub layouts. This is clearly not a good approach: only the widgets with the corresponding object name will be actually deleted, while the others will still be "owned" by their previous parent, will still be visible at their previous position and their geometries won't be updated since they're not managed by the layout anymore.
There are multiple solutions to your question, all depending on your needs.
If you need to remove rows from a layout, I'd consider setting the object name on the layout instead, and look for it using self.findChild().
Also consider that, while Qt allows setting the same object name for more than one object, that's not suggested.
Finally, while using del is normally enough, it's usually better to call deleteLater() for all Qt objects, which ensures that Qt correctly removes all objects (and related parentship/connections).
Another possibility, for this specific case, is to use a QFormLayout.

How to force a layout to resize with the wizard page

I have a wizard page that displays two tables and a hand full of buttons. Part of my tables scroll bars and the bottom buttons keep getting hidden and I have to expand the window when that page is reached. I am not sure if the overall layout scheme I have for this page is just set up improperly.
How can I have the layout and widgets within the layout resize along with the window?
I am using PySide2 on Python 3.7. I have tried setting the layout as a fixed size along with reading a few other threads on this but I can't seem to reproduce the same results so that the widgets fit within the window/as the window resize they scale with it.
class Resolution_Page(QtWidgets.QWizardPage):
def __init__(self, parent=None):
super(Resolution_Page, self).__init__(parent)
self.Resolution_Wizard_Page = QtWidgets.QWizardPage()
self.Resolution_Wizard_Page.setObjectName("Resolution_Wizard_Page")
self.ResolutionTable = QtWidgets.QTableWidget()
self.ResolutionTable.setObjectName('ResolutionTable')
self.SearchTable = QtWidgets.QTableWidget()
self.SearchTable.setObjectName('SearchTable')
self.SearchTable.setColumnCount(2)
self.SearchTable.setHorizontalHeaderLabels(['Searched Field', 'Searched Value'])
self.SearchTable.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.Stretch)
self.SearchTable.setSortingEnabled(False)
self.SearchTable.setEnabled(True)
self.SearchTable.setRowCount(0)
self.next_resolution_button = QtWidgets.QPushButton('Next Resolution')
self.next_resolution_button.setObjectName('next_resolution_button')
self.next_resolution_button.setMaximumWidth(200)
self.next_search_button = QtWidgets.QPushButton('Next Page')
self.next_search_button.setObjectName('next_search_button')
self.resolve_button = QtWidgets.QPushButton('Resolve Selection')
self.resolve_button.setEnabled(False)
self.resolve_button.setObjectName('resolve_button')
self.Search = QtWidgets.QPushButton('Search')
self.Search.setObjectName('Search')
self.previous_search_result_button = QtWidgets.QPushButton('Previous Page')
self.previous_search_result_button.setObjectName('previous_search_result_button')
self.previous_resolution_button = QtWidgets.QPushButton('Previous Resolution')
self.previous_resolution_button.setObjectName('previous_resolution_button')
self.previous_resolution_button.setMaximumWidth(200)
self.SearchTable.setMinimumWidth(300)
layout = QtWidgets.QWidget(self)
root_layout = QtWidgets.QVBoxLayout(layout)
central_tables = QtWidgets.QHBoxLayout()
middle_buttons = QtWidgets.QVBoxLayout()
bottom_buttons = QtWidgets.QHBoxLayout()
middle_buttons.addWidget(self.previous_search_result_button)
middle_buttons.addWidget(self.Search)
middle_buttons.addWidget(self.next_search_button)
central_tables.addWidget(self.SearchTable, stretch=1)
central_tables.addLayout(middle_buttons)
central_tables.addWidget(self.ResolutionTable, stretch=1)
bottom_buttons.addWidget(self.previous_resolution_button)
bottom_buttons.addWidget(self.resolve_button)
bottom_buttons.addWidget(self.next_resolution_button)
bottom_buttons.setAlignment(QtCore.Qt.AlignBottom)
root_layout.addLayout(central_tables)
root_layout.addLayout(bottom_buttons)
layout.setFixedSize(1200,600)
You should parent your top widget to a main layout and prevent using a fixed size for main widget
Create a main layout and parent your widget to it:
main_layout = QtWidgets.QVBoxLayout(self)
#layout = QtWidgets.QWidget(self)
layout = QtWidgets.QWidget()
main_layout.addWidget(layout)
remove this line:
layout.setFixedSize(1200,600)

Wrong widget order using vbox layout PyQt

I am trying to put a QLabel widget on top of (ie before) a QLineEdit widget edit.
But it keeps appearing after the QLineEdit widget. My code,
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Flags
self.randFlag = False
self.sphereFlag = False
self.waterFlag = False
# Poly names
self.pNames = QtGui.QLabel("Import file name", self) # label concerned
self.polyNameInput = QtGui.QLineEdit(self) # line edit concerned
# Polytype selection
self.polyTypeName = QtGui.QLabel("Particle type", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("")
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
def onActivated(self, text):
# Do loads of clever stuff that I'm not at liberty to share with you
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
poly = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The window I get is below.
What am I missing? I thought QVbox allowed to stack things vertically in the order that you add the items to the main widget. (Are these sub-widget objects called widgets?)
The problem is because you are adding self.pNames label to layout twice.
#portion of your code
...
self.layout.addWidget(self.pNames) # here
self.layout.addWidget(self.polyNameInput)
self.layout.addWidget(self.pNames) # and here
self.layout.addWidget(self.polyTypeName)
self.layout.addWidget(polyType)
self.layout.addStretch()
...
The first time you add the QLabel, it gets added before the LineEdit and when you add it second time, it just moves to the bottom of LineEdit. This happens because there is only one object of QLabel which is self.pNames. It can be added to only one location. If you want to use two labels, consider creating two separate objects of QLabel

Categories