I have a QDialog that has multiple QLineEdit and QPushButton widgets. PySide highlights both, the first QLineEdit and the first QPushButton.
How can I make it focus on one thing at a time?
I'd like to be able to tab to the QPushButton. So, turning off focus isn't what I want, because that stopped me from being able to tab into the button.
Here's an image of the problem:
And here's the code:
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from PySide.QtCore import *
from PySide.QtGui import *
class UI(object):
def setupUi(self, SessionInfo):
SessionInfo.setWindowTitle("Session Info")
# SessionInfo.resize(313, 159)
self.sessionLabel = QLabel(SessionInfo)
self.sessionLabel.setText("Session information:")
self.sessionId = QLineEdit(SessionInfo)
self.presetsLabel = QLabel(SessionInfo)
self.presetsLabel.setText("Presets:")
self.presetsButton = QPushButton(SessionInfo)
self.presetsButton.setText("Choose presets file")
self.saveButton = QPushButton(SessionInfo)
self.saveButton.setText("Begin session")
self.saveButton.setFocusPolicy(Qt.StrongFocus)
spacerItem = QSpacerItem(40, 20,
QSizePolicy.Expanding,
QSizePolicy.Minimum)
self.gridLayout = QGridLayout(SessionInfo)
self.gridLayout.addWidget(self.sessionLabel, 0, 0, 1, 1)
self.gridLayout.addWidget(self.sessionId, 0, 1, 1, 2)
self.gridLayout.addWidget(self.presetsLabel, 1, 0, 1, 1)
self.gridLayout.addWidget(self.presetsButton, 1, 1, 1, 2)
self.gridLayout.addItem(spacerItem, 2, 0, 1, 2)
self.gridLayout.addWidget(self.saveButton, 2, 2, 1, 1)
def main():
import sys
app = QApplication(sys.argv)
Dialog = QDialog()
sessionInfo = UI()
sessionInfo.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
It seems you cannot use a QDialog for that because it always wants to have exactly one default button, even right from the beginning. QWidget instead is more nice but doesn't set the default property of its button when they get focus. However, you can do that by yourself overriding focusInEventand focusOutEvent in your custom buttons.
Example:
from PySide.QtGui import *
class FocusButton(QPushButton):
def __init__(self, *args, **kwargs):
QPushButton.__init__(self, *args, **kwargs)
def focusInEvent(self, event):
self.setDefault(True)
QPushButton.focusInEvent(self, event)
def focusOutEvent(self, event):
self.setDefault(False)
QPushButton.focusOutEvent(self, event)
app = QApplication([])
dlg = QWidget()
label1 = QLabel("Label one")
label2 = QLabel("Label two")
edit1 = QLineEdit(dlg)
button1 = FocusButton("Button one")
button2 = FocusButton("Button two")
spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
gridLayout = QGridLayout(dlg)
gridLayout.addWidget(label1, 0, 0, 1, 1)
gridLayout.addWidget(edit1, 0, 1, 1, 2)
gridLayout.addWidget(label2, 1, 0, 1, 1)
gridLayout.addWidget(button1, 1, 1, 1, 2)
gridLayout.addItem(spacerItem, 2, 0, 1, 2)
gridLayout.addWidget(button2, 2, 2, 1, 1)
dlg.show()
app.exec_()
And the buttons are not highlighted initially, only if they get the focus.
Related
When I run the following script, named test2.py, if I click on Ok or Cancel button the window closes but I don't get the prompt. I think I forgot something stupid ...
This is the test2.py script:
from PyQt5 import Qt
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
import sys
import unittest
class MainAppWindow(QMainWindow):
def __init__(self):
super().__init__()
dialog = Qt.QDialog()
buttonbox = Qt.QDialogButtonBox(Qt.QDialogButtonBox.Ok | Qt.QDialogButtonBox.Cancel)
layout = Qt.QVBoxLayout()
param_box = Qt.QGroupBox('Iterate over parameters:')
pblayout = Qt.QVBoxLayout()
pblayout.setContentsMargins(0, 0, 0, 0)
scroll = Qt.QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameStyle(scroll.NoFrame)
scroll.setViewportMargins(0, 0, 0, 0)
pblayout.addWidget(scroll)
param_lay = Qt.QGridLayout()
wid = Qt.QWidget()
scroll.setWidget(wid)
wid.setLayout(param_lay)
param_box.setLayout(pblayout)
layout.addWidget(param_box)
layout.addWidget(buttonbox)
dialog.setLayout(layout)
buttonbox.accepted.connect(dialog.accept)
buttonbox.rejected.connect(dialog.reject)
param_lay.addWidget(Qt.QLabel('iter. / database:'), 0, 0, 1, 3)
param_lay.addWidget(Qt.QLabel('iter.:'), 0, 3, 1, 2)
param_lay.setColumnStretch(2, 1)
param_lay.setColumnStretch(4, 1)
param_lay.setRowStretch(0, 0)
res = dialog.exec_()
if res != dialog.Accepted:
print('dialog not accepted')
else:
print('dialog accepted')
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainAppWindow()
sys.exit(app.exec_())
When I run the script with python3 test2.py, I observe in stdout the dialog accepted or dialog not accepted depending on the button clicked, the window closes but I don't get the prompt. I have to stop manually (CtrLZ) to get the prompt.
The problem is that you have 2 eventloops: dialog.exec_() and app.exec_() so when the first eventloop finishes the other eventloop starts. On the other hand, it is useless to create a class that inherits from MainAppWindow since the window is never used or shown.
One possible solution is to start only one eventloop:
import sys
from PyQt5.QtWidgets import (
QApplication,
QDialog,
QDialogButtonBox,
QGridLayout,
QGroupBox,
QLabel,
QMainWindow,
QScrollArea,
QVBoxLayout,
QWidget,
)
def main():
dialog = QDialog()
buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
layout = QVBoxLayout(dialog)
param_box = QGroupBox("Iterate over parameters:")
pblayout = QVBoxLayout()
pblayout.setContentsMargins(0, 0, 0, 0)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameStyle(scroll.NoFrame)
scroll.setViewportMargins(0, 0, 0, 0)
pblayout.addWidget(scroll)
param_lay = QGridLayout()
wid = QWidget()
scroll.setWidget(wid)
wid.setLayout(param_lay)
param_box.setLayout(pblayout)
layout.addWidget(param_box)
layout.addWidget(buttonbox)
buttonbox.accepted.connect(dialog.accept)
buttonbox.rejected.connect(dialog.reject)
param_lay.addWidget(QLabel("iter. / database:"), 0, 0, 1, 3)
param_lay.addWidget(QLabel("iter.:"), 0, 3, 1, 2)
param_lay.setColumnStretch(2, 1)
param_lay.setColumnStretch(4, 1)
param_lay.setRowStretch(0, 0)
res = dialog.exec_()
if res != dialog.Accepted:
print("dialog not accepted")
else:
print("dialog accepted")
if __name__ == "__main__":
app = QApplication(sys.argv)
main()
I want to do now is find a way to increment the QLineEdit box (Step_Box) with the corresponding value of one of the buttons (0.01, 1, 10...). Each time I press on one of the buttons, the entry on QLineEdit must increment by that value. How should I do this?
# -*- coding: utf8 -*-
import sys
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class XY_Buttons(QPushButton):
def __init__(self, name):
super(XY_Buttons, self).__init__()
self.clicked.connect(self.is_clicked)
self.setText(str(name))
def is_clicked(self):
print("A")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
xstep=0.0
Step_Box = QLineEdit(str(xstep))
Step_Box.setReadOnly(True)
Units_Box = QComboBox()
Units_Box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(XY_Buttons(0.01), 1, 0)
XYLayout.addWidget(XY_Buttons(0.1), 1, 1)
XYLayout.addWidget(XY_Buttons(1), 1, 2)
XYLayout.addWidget(XY_Buttons(10), 2, 0)
XYLayout.addWidget(XY_Buttons(100), 2, 1)
XYLayout.addWidget(XY_Buttons(1000), 2, 2)
# XYLayout.addWidget(Clear_Button(), 0, 2, 1, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(Step_Box, 0, 1)
XYLayout.addWidget(Units_Box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
You need some reference to the instance of QLineEdit i.e. Step_Box in order to change their values from the button click, since you have a custom button class, you can just pass the instance of the line edit and store it there, then you can just increment the value from the slot to which you have connected the clicked signal of the buttons.
import sys
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class XY_Buttons(QPushButton):
def __init__(self, name, lineEdit:QLineEdit=None):
super(XY_Buttons, self).__init__()
self.clicked.connect(self.is_clicked)
self.setText(str(name))
self.lineEdit = lineEdit
def is_clicked(self):
print("A")
self.lineEdit.setText(str(float(self.lineEdit.text())+ float(self.text())))
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
xstep=0.0
Step_Box = QLineEdit(str(xstep))
Step_Box.setReadOnly(True)
Units_Box = QComboBox()
Units_Box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(XY_Buttons(0.01, Step_Box), 1, 0)
XYLayout.addWidget(XY_Buttons(0.1, Step_Box), 1, 1)
XYLayout.addWidget(XY_Buttons(1, Step_Box), 1, 2)
XYLayout.addWidget(XY_Buttons(10, Step_Box), 2, 0)
XYLayout.addWidget(XY_Buttons(100, Step_Box), 2, 1)
XYLayout.addWidget(XY_Buttons(1000, Step_Box), 2, 2)
# XYLayout.addWidget(Clear_Button(), 0, 2, 1, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(Step_Box, 0, 1)
XYLayout.addWidget(Units_Box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
But generally, passing reference like this is not considered a good practice, so it'd have been better if you connected it from within the class where the instance was created.
I would advise to introduce a clear separation of the data, i.e. the currently selected step, from the GUI. A common approach is Model-View-Controller (MVC).
I think it is necessary for any GUI application, as it otherwise leads to spaghetti code of widgets talking to each other, patching out bugs.
There are several View and Model components already available inside Qt5 (QStringListModel + QListView for example), but it's trivial to make our own.
Here I introduce the FooModel and the corresponding view, along with buttons (Controller) that attempts to modify the model.
# -*- coding: utf8 -*-
import sys
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import (
QApplication,
QMainWindow,
QWidget,
QGridLayout,
QPushButton,
QLineEdit,
QLabel,
QComboBox,
)
class FooChanger(QPushButton):
# Note: This is the controller. It doesn't try to be clever and modify the view.
# It just modifies the model. If the model wishes to signal to any view component, that's the models responsibility.
def __init__(self, foo_model, value):
super().__init__(str(value))
self.clicked.connect(self.is_clicked)
self.model = foo_model
self.value = value
def is_clicked(self):
self.model.set_value(self.value)
class FooView(QLabel):
# This is the view. It does try to be clever either. It just updates information when the model signals.
def __init__(self, model):
super().__init__("hejsan")
self.model = model
self.model.data_changed.connect(self.refresh)
self.refresh() # initial refresh
def refresh(self):
self.setText(str(self.model.value))
class FooModel(QObject):
# This is our simple model. It only has a signal, and uses it to indicate when the data has been modified.
# Make sure to use the set_value function and don't poke at self.value directly!
data_changed = pyqtSignal()
def __init__(self, initial_value):
super().__init__()
# We are currently just storing one piece of information. The infamous Foo value!
self.value = initial_value
def set_value(self, new_value):
self.value = new_value
self.data_changed.emit()
class MainWindow(QMainWindow):
def __init__(self, model):
super().__init__()
self.setWindowTitle("Suruga Controller")
OuterLayout = QGridLayout() # Parent layout
ArrowsLayout = QGridLayout() # Arrows layout -- in here are the XY controller
UpDownLayout = QGridLayout() # Up and Down layout -- z controller
XYLayout = QGridLayout() # XY options layout -- step size and others
ZLayout = QGridLayout() # Z options layout -- step size and others
# Adding Widgets for ArrowsLayout
ArrowsLayout.addWidget(QPushButton("Up"), 0, 1)
ArrowsLayout.addWidget(QPushButton("Down"), 2, 1)
ArrowsLayout.addWidget(QPushButton("Left"), 1, 0)
ArrowsLayout.addWidget(QPushButton("Right"), 1, 2)
# Adding Widgets for XYLayout
step_box = FooView(model)
unix_box = QComboBox()
unix_box.addItems([u"10E-6 (µm) ", "10E-3 (cm)"])
XYLayout.addWidget(FooChanger(model, 0.01), 1, 0)
XYLayout.addWidget(FooChanger(model, 0.1), 1, 1)
XYLayout.addWidget(FooChanger(model, 1), 1, 2)
XYLayout.addWidget(FooChanger(model, 10), 2, 0)
XYLayout.addWidget(FooChanger(model, 100), 2, 1)
XYLayout.addWidget(FooChanger(model, 1000), 2, 2)
XYLayout.addWidget(QLabel("Step is:"), 0, 0, 1, 2)
XYLayout.addWidget(step_box, 0, 1)
XYLayout.addWidget(unix_box, 3, 1)
# Nesting all layouts
OuterLayout.addLayout(ArrowsLayout, 0, 0)
OuterLayout.addLayout(XYLayout, 0, 1)
OuterLayout.addLayout(UpDownLayout, 1, 0)
OuterLayout.addLayout(ZLayout, 1, 1)
widget = QWidget()
widget.setLayout(OuterLayout)
self.setCentralWidget(widget)
app = QApplication(sys.argv)
foo = FooModel(3.1415)
window = MainWindow(foo) # The view acts on our foo.
window.show()
app.exec()
There is no need for cross-talk between widgets here. It's just a view that only displays current data (it doesn't even care how the data got modified, it just always works), a controller that just informs the model that the user requested a change, and a model that is blissfully ignorant of the existence of either widgets or buttons.
I have the following code which shows me 7 of the widgets in a history window of my application. Each widget has a name, icon and an address to a previously opened file. I want all widgets to be clickable and write a function which exactly knows which widget was clicked. I have no clue on how to make a whole widget act as a single clickable item.
This is my code for showing the widgets.
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
for i in range (7):
self.gridLayoutWidget = QWidget(self)
self.gridLayoutWidget.setGeometry(QRect(40, 40 + i * 60, 350, 40))
self.gridLayout = QGridLayout(self.gridLayoutWidget)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.Label1 = QLabel(self.gridLayoutWidget)
self.Label1.setText("the name of the file")
self.Label1.setFont(QFont('Arial', 14))
self.gridLayout.addWidget(self.Label1, 0, 1, 1, 9)
self.Label2 = QLabel(self.gridLayoutWidget)
self.Label2.setText("the address of the file")
self.gridLayout.addWidget(self.Label2, 1, 1, 1, 9)
self.im = QPixmap("img.ico")
self.icon = QLabel()
self.icon.setPixmap(self.im)
self.gridLayout.addWidget(self.icon , 0, 0, 2, 1)
In this case it is better to create a class that inherits from widget and implements the logic of the complex widget to avoid repetitive code that can cause problems (for example self.Label2 what does QLabel refer to? Does it refer to the first, the second, ...? , since it refers to the latter). On the other hand, that allows us to override the mouseReleaseEvent method, causing a signal to be emitted that later allows us to identify the widget using the sender method.
class GridLayoutWidget(QWidget):
clicked = pyqtSignal()
def __init__(self, parent=None):
super().__init__(parent)
self.Label1 = QLabel(text="the name of the file", font=QFont("Arial", 14))
self.Label2 = QLabel(text="the address of the file")
self.icon = QLabel(pixmap=QPixmap("img.ico"))
self.gridLayout = QGridLayout(self)
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.gridLayout.addWidget(self.Label1, 0, 1, 1, 9)
self.gridLayout.addWidget(self.Label2, 1, 1, 1, 9)
self.gridLayout.addWidget(self.icon, 0, 0, 2, 1)
def mouseReleaseEvent(self, event):
super().mouseReleaseEvent(event)
self.clicked.emit()
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
rect = QRect(40, 40, 350, 40)
for i in range(7):
widget = GridLayoutWidget(self)
widget.setGeometry(rect.translated(0, 60 * i))
widget.clicked.connect(self.handle_clicked)
self.resize(640, 480)
#pyqtSlot()
def handle_clicked(self):
widget = self.sender()
print(widget)
I'm trying to get a lineEdit that have a cursor in it when a button is clicked. For example: I run application, put a cursor in one lineEdit, and when clicking a button - some text should be set in chosen lineEdit.
I've tried with keyboardGrabber, but it returns button =None.
import sys
from PyQt5 import QtWidgets, QtCore
class Mainwindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("window")
self.lineEdit1 = QtWidgets.QLineEdit()
self.lineEdit2 = QtWidgets.QLineEdit()
self.lineEdit3 = QtWidgets.QLineEdit()
self.pushButton = QtWidgets.QPushButton()
self.label = QtWidgets.QLabel()
self.label.setText('#1')
self.frame = QtWidgets.QFrame(self)
self.frame.setGeometry(QtCore.QRect(0, 0, 200, 100))
self.gridLayout = QtWidgets.QGridLayout(self.frame)
self.gridLayout.addWidget(self.lineEdit1, 0, 0, 1, 1)
self.gridLayout.addWidget(self.lineEdit2, 0, 1, 1, 1)
self.gridLayout.addWidget(self.lineEdit3, 0, 2, 1, 1)
self.gridLayout.addWidget(self.label, 0, 3, 1, 1)
self.gridLayout.addWidget(self.pushButton, 1, 0, 1, 1)
self.pushButton.clicked.connect(self.function)
def function(self):
widget = self.keyboardGrabber()
widget.setText('some text')
if __name__ == '__main__':
app = QtWidgets.QApplication([])
application = Mainwindow()
application.show()
Try it: void QApplication::focusChanged(QWidget *old, QWidget *now)
This signal is emitted when the widget that has keyboard focus changed from old to now, i.e., because the user pressed the tab-key, clicked into a widget or changed the active window. Both old and now can be nullptr.
import sys
from PyQt5 import QtWidgets, QtCore
class Mainwindow(QtWidgets.QMainWindow): # QMainWindow QWidget
def __init__(self):
super().__init__()
QtWidgets.qApp.focusChanged.connect(self.on_focusChanged) # +++
self.setWindowTitle("window")
self.lineEdit1 = QtWidgets.QLineEdit(self)
self.lineEdit1.setFocus() # +
self.lineEdit2 = QtWidgets.QLineEdit()
self.lineEdit3 = QtWidgets.QLineEdit()
self.pushButton = QtWidgets.QPushButton()
self.label = QtWidgets.QLabel()
self.label.setText('#1')
self.frame = QtWidgets.QFrame(self)
self.setCentralWidget(self.frame) # +
self.frame.setGeometry(QtCore.QRect(0, 0, 200, 100))
self.gridLayout = QtWidgets.QGridLayout(self.frame)
self.gridLayout.addWidget(self.lineEdit1, 0, 0, 1, 1)
self.gridLayout.addWidget(self.lineEdit2, 0, 1, 1, 1)
self.gridLayout.addWidget(self.lineEdit3, 0, 2, 1, 1)
self.gridLayout.addWidget(self.label, 0, 3, 1, 1)
self.gridLayout.addWidget(self.pushButton, 1, 0, 1, 1)
self.lineFocus = ... # +++
self.pushButton.clicked.connect(self.function)
def function(self):
# widget = self.keyboardGrabber()
# widget.setText('some text')
self.lineFocus.setText('some text')
#QtCore.pyqtSlot("QWidget*", "QWidget*")
def on_focusChanged(self, old, now): # +++
#print(f"\nold: {old}, now: {now}")
self.lineFocus = old
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
application = Mainwindow()
application.show()
sys.exit(app.exec_())
I am trying to build a simple GUI with PyQt5, where I have a few widgets which I want to align using QGridLayout.
Look at the following example code which I found on some website:
import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, QApplication)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Output
As you can see, there are three labels in the first column, each spanning one cell vertically and one cell horizontally.
In the second column there are two 1x1 widgets and one 5x1 widget.
Let's say I want to place another label called test and a lineedit called testEdit below.
Naively I would modify initUI() like this:
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
test = QLabel('Test')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
testEdit = QTextEdit()
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
grid.addWidget(test, 8, 0)
grid.addWidget(testEdit, 8, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
Where I just placed the new 1x1 widgets in the 8th row, below the previous one, which produces:
Output 2
However the result is not what I would expect, since the testEdit-widget is definitely not of size 1x1 and the reviewEdit-widget is also altered.
So why doesn't it work this way?
Try it:
import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
QTextEdit, QGridLayout, QApplication,
)
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
title = QLabel('Title')
author = QLabel('Author')
review = QLabel('Review')
test = QLabel('Test')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
reviewEdit = QTextEdit()
testEdit = QTextEdit()
testEdit.setMaximumHeight(20) # +++
grid = QGridLayout()
grid.setSpacing(10)
grid.addWidget(title, 1, 0)
grid.addWidget(titleEdit, 1, 1)
grid.addWidget(author, 2, 0)
grid.addWidget(authorEdit, 2, 1)
grid.addWidget(review, 3, 0)
grid.addWidget(reviewEdit, 3, 1, 5, 1)
grid.addWidget(test, 8, 0)
grid.addWidget(testEdit, 8, 1)
self.setLayout(grid)
self.setGeometry(300, 300, 350, 300)
self.setWindowTitle('Review')
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())