I've googled around but I'm not able to find a solution to my problem.
I have a QTableWidget with 2 columns and what I'm trying to do is to make them visible to the whole widget without the horizontal scrollbar to appear.
With a picture it should be all clear:
I have used Qt Designer to create the UI and some code to fill all the widgets and other stuff.
So, first I resized th2 2 columns to the content with:
self.statTable.resizeColumnToContents(0)
self.statTable.resizeColumnToContents(1)
and it works, but then the Widget is not resizing to the 2 columns width.
This has a very easy solution in PyQt5. All you need to do is set the size adjust policy on the table when initialising the UI, and it will automatically resize to fit the contents. This can either be done via Qt Designer (in the QAbstractScrollArea section of the Property Editor), or programmatically, like this:
self.statTable.setSizeAdjustPolicy(
QtWidgets.QAbstractScrollArea.AdjustToContents)
You then just need to do:
self.statTable.resizeColumnsToContents()
whenever the table is re-populated.
For PyQt4, everything has to be calculated manually, and a few hacks are also required to get completely consistent results. The demo script below works okay for me, but YMMV:
import random
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.table = QtGui.QTableWidget(5, 2, self)
self.button = QtGui.QPushButton('Populate', self)
self.button.clicked.connect(self.populate)
layout = QtGui.QGridLayout(self)
layout.addWidget(self.table, 0, 0)
layout.addWidget(self.button, 1, 0)
layout.setColumnStretch(1, 1)
def populate(self):
words = 'Red Green Blue Yellow Black White Purple'.split()
length = random.randint(2, len(words))
self.table.setRowCount(random.randint(3, 30))
for column in range(self.table.columnCount()):
for row in range(self.table.rowCount()):
item = QtGui.QTableWidgetItem(' '.join(
random.sample(words, random.randint(1, length))))
self.table.setItem(row, column, item)
self.table.setVisible(False)
self.table.verticalScrollBar().setValue(0)
self.table.resizeColumnsToContents()
self.table.setVisible(True)
self.setTableWidth()
def setTableWidth(self):
width = self.table.verticalHeader().width()
width += self.table.horizontalHeader().length()
if self.table.verticalScrollBar().isVisible():
width += self.table.verticalScrollBar().width()
width += self.table.frameWidth() * 2
self.table.setFixedWidth(width)
def resizeEvent(self, event):
self.setTableWidth()
super(Window, self).resizeEvent(event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(700, 150, 800, 400)
window.show()
sys.exit(app.exec_())
For the autoadjust settings on the table widget in Qt Designer, you canlook in the object inspector for the table widget you can drill down to it as shown below.
(PyQt5)
The issue for me is that my cells in the right-most column are multi-line (QPlainTextEdit), and I wanted word-wrapping... but I also wanted this right-most column to extend to fill the parent container.
It seems you can do everything you need in PyQt5 designer, in the Property editor for your QTableView:
in the "QTableView" section check "wordWrap"
in the "QAbstractScroll" section check "AdjustToContents" (as mentioned by Crap Phone)
in the "Header" section check "horizontalHeaderStretchLastSection"
This then generates the following sort of code:
self.history_table_view.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
self.history_table_view.horizontalHeader().setStretchLastSection(True )
"word wrap = True" appears to be the default setting, so nothing is shown, but it would be this:
self.history_table_view.setWordWrap(True)
Use your_tablewidget.resizeColumnsToContents() every single time after you call your_tablewidget.setItem(). You don't need any other setting.
you know just try, tableWidget.resize(1200, 600) *you can change resolution but it is your answer...
Related
I want my QTableWidget to:
Fill all available space inside the window
Show vertical scroll bar if it's longer than the provided space
Adapt to changing window geometry
Do not show blank white space underneath, if it's smaller than the provided space (without stretching bottom row).
So basically what I want to see:
Short table without white space
Long table fills no less no more than available and shows scroll bar
But I can't achieve these two states simultaneously.
What I get instead:
Ugly white space beneath the table
Table stretches the window itself
The difference in the two behaviors is in calculating table height manually. If I do so, a long table stretches the window, but otherwise it tries to fill all unused space with blank white.
Here is my minimal code:
from PyQt6.QtWidgets import *
from PyQt6.QtCore import *
from PyQt6.QtGui import *
import os
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(100, 100, 200, 100)
# self.tableLabel = QLabel() # I thought it might help somehow
self.table = QTableWidget(4, 1)
self.setTable(4)
self.table.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.fewRowsBtn = QPushButton('Few rows')
self.manyRowsBtn = QPushButton('Many rows')
self.button = QPushButton('Button')
self.button.setFixedWidth(120)
self.fewRowsBtn.clicked.connect(lambda: self.setTable(4))
self.manyRowsBtn.clicked.connect(lambda: self.setTable(20))
self.map = QLabel()
canvas = QPixmap(320, 320)
canvas.fill()
self.map.setPixmap(canvas)
leftLayout = QVBoxLayout()
leftLayout.addWidget(self.fewRowsBtn)
leftLayout.addWidget(self.manyRowsBtn)
leftLayout.addWidget(self.map)
rightLayout = QVBoxLayout()
rightLayout.addWidget(self.table)
rightLayout.addWidget(self.button) #, alignment=Qt.AlignmentFlag.AlignBottom)
generalLayout = QHBoxLayout()
generalLayout.addLayout(leftLayout)
generalLayout.addLayout(rightLayout)
container = QWidget()
container.setLayout(generalLayout)
self.setCentralWidget(container)
def setTable(self, rows):
self.table.setRowCount(rows)
# self.table.setFixedHeight(self.tableHeight())
def tableWidth(self):
tableWidth = self.table.verticalHeader().width() + \
self.table.horizontalHeader().length() + \
self.table.frameWidth() * 2
app.processEvents() # Otherwise verticalScrollBar().isVisible()
app.processEvents() # lags one event
if self.table.verticalScrollBar().isVisible():
tableWidth += self.table.verticalScrollBar().width()
return tableWidth
def tableHeight(self):
tableHeight = self.table.verticalHeader().length() + \
self.table.horizontalHeader().height() + \
self.table.frameWidth() * 2
return tableHeight
app = QApplication(os.sys.argv)
mywindow = MainWindow()
mywindow.show()
app.exec()
I tried playing with different size policies, but all of them give the behavior mentioned above.
I had a vague idea about placing the table in a label, but I don't know how it could help.
Additionally, one other behavior I don't understand is why adding top or bottom alignment for the button under the table makes available space half the height of the window. As if because of that the QVBoxLayout starts distributing space evenly between it's two items.
I think getting the value of the available space in a given window geometry migth help me adjust table size manually. So at least an advise how to do that could help me.
I use Python 3.9.12 and PyQt 6.3.0
I want to create a child container layout which will contains 2 widgets. Those 2 widgets should be placed right next to each other but my current setup still has some spacing in between.
I have already set the spacing to 0 setSpacing(0). And setContentsMargins(0,0,0,0) doesn't helped.
I am using PyQt5 but it shouldn't be a problem converting c++ code.
As you can see in the picture there is still a small gap:
(Left: LineEdit - Right: PushButton)
import PyQt5.QtCore as qc
import PyQt5.QtGui as qg
import PyQt5.QtWidgets as qw
import sys
class Window(qw.QWidget):
def __init__(self):
qw.QWidget.__init__(self)
self.initUI()
def initUI(self):
gridLayout = qw.QGridLayout()
height = 20
self.label1 = qw.QLabel("Input:")
self.label1.setFixedHeight(height)
gridLayout.addWidget(self.label1, 0, 0)
# Child Container
childGridLayout = qw.QGridLayout()
childGridLayout.setContentsMargins(0,0,0,0)
childGridLayout.setHorizontalSpacing(0)
self.lineEdit1 = qw.QLineEdit()
self.lineEdit1.setFixedSize(25, height)
childGridLayout.addWidget(self.lineEdit1, 0, 0)
self.pushButton1 = qw.QPushButton("T")
self.pushButton1.setFixedSize(20, height)
childGridLayout.addWidget(self.pushButton1, 0, 1)
# -----------------
gridLayout.addItem(childGridLayout, 0,1)
self.setLayout(gridLayout)
if __name__ == '__main__':
app = qw.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
The QT documentation says:
By default, QLayout uses the values provided by the style. On most platforms, the margin is 11 pixels in all directions.
Ref:http://doc.qt.io/qt-4.8/qlayout.html#setContentsMargins
So you may need to use "setHorizontalSpacing(int spacing)" for horizontal space and "setVerticalSpacing(int spacing)" for vertical.
Based on the documentation, this may delete space in your case.
Ref:http://doc.qt.io/qt-4.8/qgridlayout.html#horizontalSpacing-prop
If not resolved, there is an option to override style settings for space (from where the layout gets).... I think this is tedious
If you want to provide custom layout spacings in a QStyle subclass, implement a slot called layoutSpacingImplementation() in your subclass.
More detials:
http://doc.qt.io/qt-4.8/qstyle.html#layoutSpacingImplementation
I currently have a QScrollArea defined by:
self.results_grid_scrollarea = QScrollArea()
self.results_grid_widget = QWidget()
self.results_grid_layout = QGridLayout()
self.results_grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
self.results_grid_widget.setLayout(self.results_grid_layout)
self.results_grid_scrollarea.setWidgetResizable(True)
self.results_grid_scrollarea.setWidget(self.results_grid_widget)
self.results_grid_scrollarea.setViewportMargins(0,20,0,0)
which sits quite happily nested within other layouts/widgets, resizes as expected, etc.
To provide headings for the grid columns, I'm using another QGridLayout positioned directly above the scroll area - this works... but looks a little odd, even when styled appropriately, especially when the on-demand (vertical) scrollbar appears or disappears as needed and the headers no longer line up correctly with the grid columns. It's an aesthetic thing I know... but I'm kinda picky ;)
Other widgets are added/removed to the self.results_grid_layout programatically elsewhere. The last line above I've just recently added as I thought it would be easy to use the created margin area, the docs for setViewportMargins state:
Sets margins around the scrolling area. This is useful for applications such as spreadsheets with "locked" rows and columns. The marginal space is is left blank; put widgets in the unused area.
But I cannot for the life of me work out how to actually achieve this, and either my GoogleFu has deserted me today, or there's little information/examples out there on how to actually achieve this.
My head is telling me I can assign just one widget, controlled by a layout (containing any number of other widgets) to the scrollarea - as I have done. If I add say a QHeaderview for example to row 0 of the gridlayout, it will just appear below the viewport's margin and scroll with the rest of the layout? Or am I missing something and just can't see the wood for the trees?
I'm just learning Python/Qt, so any help, pointers and/or examples (preferably with Python but not essential) would be appreciated!
Edit: Having followed the advice given so far (I think), I came up with the following little test program to try things out:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setMinimumSize(640, 480)
self.container_widget = QWidget()
self.container_layout = QVBoxLayout()
self.container_widget.setLayout(self.container_layout)
self.setCentralWidget(self.container_widget)
self.info_label = QLabel(
"Here you can see the problem.... I hope!\n"
"Once the window is resized everything behaves itself.")
self.info_label.setWordWrap(True)
self.headings_widget = QWidget()
self.headings_layout = QGridLayout()
self.headings_widget.setLayout(self.headings_layout)
self.headings_layout.setContentsMargins(1,1,0,0)
self.heading_label1 = QLabel("Column 1")
self.heading_label1.setContentsMargins(16,0,0,0)
self.heading_label2 = QLabel("Col 2")
self.heading_label2.setAlignment(Qt.AlignCenter)
self.heading_label2.setMaximumWidth(65)
self.heading_label3 = QLabel("Column 3")
self.heading_label3.setContentsMargins(8,0,0,0)
self.headings_layout.addWidget(self.heading_label1,0,0)
self.headings_layout.addWidget(self.heading_label2,0,1)
self.headings_layout.addWidget(self.heading_label3,0,2)
self.headings_widget.setStyleSheet(
"background: green; border-bottom: 1px solid black;" )
self.grid_scrollarea = QScrollArea()
self.grid_widget = QWidget()
self.grid_layout = QGridLayout()
self.grid_layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
self.grid_widget.setLayout(self.grid_layout)
self.grid_scrollarea.setWidgetResizable(True)
self.grid_scrollarea.setWidget(self.grid_widget)
self.grid_scrollarea.setViewportMargins(0,30,0,0)
self.headings_widget.setParent(self.grid_scrollarea)
### Add some linedits to the scrollarea just to test
rows_to_add = 10
## Setting the above to a value greater than will fit in the initial
## window will cause the lineedits added below to display correctly,
## however - using the 10 above, the lineedits do not expand to fill
## the scrollarea's width until you resize the window horizontally.
## What's the best way to fix this odd initial behaviour?
for i in range(rows_to_add):
col1 = QLineEdit()
col2 = QLineEdit()
col2.setMaximumWidth(65)
col3 = QLineEdit()
row = self.grid_layout.rowCount()
self.grid_layout.addWidget(col1,row,0)
self.grid_layout.addWidget(col2,row,1)
self.grid_layout.addWidget(col3,row,2)
### Define Results group to hold the above sections
self.test_group = QGroupBox("Results")
self.test_layout = QVBoxLayout()
self.test_group.setLayout(self.test_layout)
self.test_layout.addWidget(self.info_label)
self.test_layout.addWidget(self.grid_scrollarea)
### Add everything to the main layout
self.container_layout.addWidget(self.test_group)
def resizeEvent(self, event):
scrollarea_vpsize = self.grid_scrollarea.viewport().size()
scrollarea_visible_size = self.grid_scrollarea.rect()
desired_width = scrollarea_vpsize.width()
desired_height = scrollarea_visible_size.height()
desired_height = desired_height - scrollarea_vpsize.height()
new_geom = QRect(0,0,desired_width+1,desired_height-1)
self.headings_widget.setGeometry(new_geom)
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == '__main__':
main()
Is something along these lines the method to which you were pointing? Everything works as expected as is exactly what I was after, except for some odd initial behaviour before the window is resized by the user, once it is resized everything lines up and is fine.
I'm probably over-thinking again or at least overlooking something... any thoughts?
I had a similar problem and solved it a little differently. Instead of using one QScrollArea I use two and forward a movement of the lower scroll area to the top one. What the code below does is
It creates two QScrollArea widgets in a QVBoxLayout.
It disables the visibility of the scroll bars of the top QScrollArea and assigns it a fixed height.
Using the valueChanged signal of the horizontal scroll bar of the lower QScrollArea it is possible to "forward" the horizontal scroll bar value from the lower QScrollArea to the top one resulting a fixed header at the top of the window.
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
widget = QWidget()
self.setCentralWidget(widget)
vLayout = QVBoxLayout()
widget.setLayout(vLayout)
# TOP
scrollAreaTop = QScrollArea()
scrollAreaTop.setWidgetResizable(True)
scrollAreaTop.setFixedHeight(30)
scrollAreaTop.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollAreaTop.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollAreaTop.setWidget(QLabel(" ".join([str(i) for i in range(100)])))
# BOTTOM
scrollAreaBottom = QScrollArea()
scrollAreaBottom.setWidgetResizable(True)
scrollAreaBottom.setWidget(QLabel("\n".join([" ".join([str(i) for i in range(100)]) for _ in range(10)])))
scrollAreaBottom.horizontalScrollBar().valueChanged.connect(lambda value: scrollAreaTop.horizontalScrollBar().setValue(value))
vLayout.addWidget(scrollAreaTop)
vLayout.addWidget(scrollAreaBottom)
You may be over-thinking things slightly.
All you need to do is use the geometry of the scrollarea's viewport and the current margins to calculate the geometry of any widgets you want to place in the margins.
The geometry of these widgets would also need to be updated in the resizeEvent of the scrollarea.
If you look at the source code for QTableView, I think you'll find it uses this method to manage its header-views (or something very similar).
EDIT
To deal with the minor resizing problems in your test case, I would advise you to read the Coordinates section in the docs for QRect (in particular, the third paragraph onwards).
I was able to get more accurate resizing by rewriting your test case like this:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setMinimumSize(640, 480)
self.container_widget = QWidget()
self.container_layout = QVBoxLayout()
self.container_widget.setLayout(self.container_layout)
self.setCentralWidget(self.container_widget)
self.grid_scrollarea = ScrollArea(self)
self.test_group = QGroupBox("Results")
self.test_layout = QVBoxLayout()
self.test_group.setLayout(self.test_layout)
self.test_layout.addWidget(self.grid_scrollarea)
self.container_layout.addWidget(self.test_group)
class ScrollArea(QScrollArea):
def __init__(self, parent=None):
QScrollArea.__init__(self, parent)
self.grid_widget = QWidget()
self.grid_layout = QGridLayout()
self.grid_widget.setLayout(self.grid_layout)
self.setWidgetResizable(True)
self.setWidget(self.grid_widget)
# save the margin values
self.margins = QMargins(0, 30, 0, 0)
self.setViewportMargins(self.margins)
self.headings_widget = QWidget(self)
self.headings_layout = QGridLayout()
self.headings_widget.setLayout(self.headings_layout)
self.headings_layout.setContentsMargins(1,1,0,0)
self.heading_label1 = QLabel("Column 1")
self.heading_label1.setContentsMargins(16,0,0,0)
self.heading_label2 = QLabel("Col 2")
self.heading_label2.setAlignment(Qt.AlignCenter)
self.heading_label2.setMaximumWidth(65)
self.heading_label3 = QLabel("Column 3")
self.heading_label3.setContentsMargins(8,0,0,0)
self.headings_layout.addWidget(self.heading_label1,0,0)
self.headings_layout.addWidget(self.heading_label2,0,1)
self.headings_layout.addWidget(self.heading_label3,0,2)
self.headings_widget.setStyleSheet(
"background: green; border-bottom: 1px solid black;" )
rows_to_add = 10
for i in range(rows_to_add):
col1 = QLineEdit()
col2 = QLineEdit()
col2.setMaximumWidth(65)
col3 = QLineEdit()
row = self.grid_layout.rowCount()
self.grid_layout.addWidget(col1,row,0)
self.grid_layout.addWidget(col2,row,1)
self.grid_layout.addWidget(col3,row,2)
def resizeEvent(self, event):
rect = self.viewport().geometry()
self.headings_widget.setGeometry(
rect.x(), rect.y() - self.margins.top(),
rect.width() - 1, self.margins.top())
QScrollArea.resizeEvent(self, event)
if __name__ == '__main__':
app = QApplication(sys.argv)
form = MainWindow()
form.show()
sys.exit(app.exec_())
I have a QHBoxLayout which I add widgets to, one by one.
I alternate by adding a Custom Widget, than a QLabel (repeating).
The QHBoxLayout is owned by a QGroupBox.
However, I notice that the spacing between the Custom Widget and the QLabel is 'irregular' when there are few widgets added to the Layout.
I intend for the QLabels to be centered in the gap between the custom widgets, but the QLabels only approach the centre as more widgets are added.
These screenshots show the two cases.
The desired case (only occuring with lots of widgets in the layout),
and the undesired case (occuring when little widgets are in the layout).
As you can see in the bottom case, the QLabels are not centered between the Custom Widgets. Instead, they are very far to the right!
What is causing this behaviour??
I believe the QGroupBox has a centered (horizontal) alignment and the QLabels have a fixed width of 10 (or 20 for the '->' QLabel) pixels (to avoid ugly overlap).
Any help at all would be greatly appreciated!
Thanks!
Specs:
python 2.7.1
PyQt4
Windows 7
The instantiation of the QHBoxLayout is absolutely normal and similiar to all examples.
Here is the code for the filling of the layout.
for i in range (0,len(Reactants)):
self.WidgetHouse.Reaction_Element_Layout.addWidget(eval('self.OverallContainer_Reactants.Reactant_'+str(i)))
# self.WidgetHouse.Reaction_Element_Layout is the QHBoxLayout
# self.OverallContainer_Reactants.Reactant_'+str(i) is a Custom Widget
if i != (len(Reactants)-1):
tmp = QtGui.QLabel('+')
tmp.setFixedWidth(10)
tmp.setAlignment(QtCore.Qt.AlignCenter)
self.WidgetHouse.Reaction_Element_Layout.addWidget(tmp)
else:
tmp = QtGui.QLabel('->')
tmp.setFixedWidth(20)
tmp.setAlignment(QtCore.Qt.AlignCenter)
self.WidgetHouse.Reaction_Element_Layout.addWidget(tmp)
EDIT:
Setting a fixed width of the QLabels (tmp.setFixedWidth(10)) is the source of the 'right anchoring'.
However, not setting a fixed width results in the same space being dedicated to QLabels and Custom Widgets in the layout, leading to an overlap of QLabels and Custom Widgets.
Identical to the code above, discluding 'tmp.setFixedWidt(10)'
What can I do to prevent this that's not completely horrible?
Can I move the Labels 'back' from the front?
(Calling .raise_() on all the custom widgets after everything was added to the layout did not work)
(Or will I have to do something like manually calculate the appropriate width of the labels based off the amount of widgets in the layout? Yuck!)
ANOTHER EDIT:
Progress:
I do not change the maximum/minimum (or fixed) width of the QLabels, but I do set their alignment to center.
Instead, I set a minimum width of the custom widget.
This fixes the apparent 'overlapping' (which wasn't really the case) and makes the Labels appear 'more centered'.
However, as you can see, the QLabels still aren't perfectly centered - too far right.
If I don't set a center alignment on the QLabels they are too far left.
What could be the problem now??
(I do not set a maximum width on the labels)
Thanks for all the help so far guys!
Here is a simple example script which is a reasonable approximation of the UI in the question, but without any of the layout issues:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.groupBox = QtGui.QGroupBox(self)
hbox = QtGui.QHBoxLayout(self.groupBox)
length = 3
for index in range(length):
hbox.addWidget(Widget(u'H\u2082O', self))
if index < length - 1:
hbox.addWidget(Label(u'+', self))
else:
hbox.addWidget(Label(u'\u2192', self))
hbox.addWidget(Widget(u'4 H\u2082O', self))
hbox.addWidget(Label(u'+', self))
hbox.addWidget(Widget(u'H\u2084O\u2082', self))
vbox = QtGui.QVBoxLayout(self)
vbox.addWidget(self.groupBox)
vbox.addStretch()
class Label(QtGui.QLabel):
def __init__(self, label, parent=None):
QtGui.QLabel.__init__(self, label, parent)
self.setAlignment(QtCore.Qt.AlignCenter)
class Widget(QtGui.QWidget):
def __init__(self, label, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setMaximumWidth(100)
layout = QtGui.QGridLayout(self)
self.label = QtGui.QLabel(label, self)
self.label.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(self.label, 0, 0, 1, 2)
self.lineEdit = QtGui.QLineEdit(self)
layout.addWidget(self.lineEdit, 1, 0, 1, 2)
self.toolButton = QtGui.QToolButton(self)
layout.addWidget(self.toolButton, 2, 0, 1, 1)
self.comboBox = QtGui.QComboBox(self)
layout.addWidget(self.comboBox, 2, 1, 1, 1)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Solution to all problems:
Give the custom widgets a fixed width.
Results in perfectly centered, not 'overlapping' widgets.
:)
Thanks for all the help guys!
I have the following code:
#!/usr/bin/env python
import sys
from PyQt4 import QtGui, QtCore
class SimfilePanel(QtGui.QWidget):
'''This class provides the simfile panel shown on the right side of the main window.'''
def __init__(self, parent=None):
'''Load song info here.'''
QtGui.QWidget.__init__(self, parent)
## Make widgets.
# Pane with simfile information.
simfileInfoPane = QtGui.QWidget()
simfileInfoPane.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
simfileInfoGrid = QtGui.QGridLayout()
simfileInfoPane.setLayout(simfileInfoGrid)
simfileInfoScrollArea = QtGui.QScrollArea()
simfileInfoScrollArea.setWidget(simfileInfoPane)
#if DEBUG: simfileInfoScrollArea.setBackgroundRole(QtGui.QPalette.Dark);
# This will change
labels = []
textfields = []
for i in range(0,20):
labels.append( QtGui.QLabel("Label "+str(i)) )
textfields.append( QtGui.QLineEdit() )
labels[i].setBuddy(textfields[i])
simfileInfoGrid.addWidget(labels[i], i, 0)
simfileInfoGrid.addWidget(textfields[i], i, 1)
## Put widgets in a grid layout.
mainvbox = QtGui.QVBoxLayout()
mainvbox.addWidget(simfileInfoScrollArea)
self.setLayout(mainvbox)
# Standalone testing
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
panel = SimfilePanel()
panel.show()
sys.exit(app.exec_())
I can't get anything that I'm putting into the simfileInfoGrid to display! They'll display if I leave out the scroll area, but I need the scroll area as I will have a lot of fields to edit in the final version and I don't want to stretch the entire window over the screen.
As you see I've tried to add a size policy to simfileInfoPane, but it doesn't seem to affect anything. The area that's supposed to contain my pane stays empty!
Add the pane to the scroll area after you've added all the grid's contents. In particular you need to call QScrollArea.setWidget after you have finished creating the widget you add.
I don't know exactly why this is the problem, but I do know that I tend to initialize widgets "bottom-up": I finish adding all the contents of a sub-layout before I ever add it to a parent layout. I believe this is Qt optimizing order of rendering but I could be wrong about that.
The code below is a patch, mostly so you can see where the one-line change is.
diff -u 1848547.py tmp2.py
--- 1848547.py 2009-12-04 11:19:09.000000000 -0800
+++ tmp2.py 2009-12-04 11:34:58.000000000 -0800
## -19,7 +19,6 ##
simfileInfoPane.setLayout(simfileInfoGrid)
simfileInfoScrollArea = QtGui.QScrollArea()
- simfileInfoScrollArea.setWidget(simfileInfoPane)
#if DEBUG:
simfileInfoScrollArea.setBackgroundRole(QtGui.QPalette.Dark)
## -33,6 +32,8 ##
simfileInfoGrid.addWidget(labels[i], i, 0)
simfileInfoGrid.addWidget(textfields[i], i, 1)
+ simfileInfoScrollArea.setWidget(simfileInfoPane)
+
## Put widgets in a grid layout.
mainvbox = QtGui.QVBoxLayout()
mainvbox.addWidget(simfileInfoScrollArea)
I have recently been struggling with the same thing, and I believe I have found the solution you are looking for.
The problem seems to be that when you add an empty widget to the scroll area, it has dimensions of zero by zero (since there is nothing inside of it).
The reason it doesn't get bigger is because there is a flag called widgetResizable that is False by default.
By simply calling setWidgetResizable(True) on the scroll area, the widget gets bigger as new items are added.
I hope this helps.