I would like to create tabs which target only a specific area within the GUI.
That is there should be an area of the GUI which is static and always present even when changing tab.
I have already created tabs as according to the following code:
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
tabs = QtGui.QTabWidget()
# Create tabs
tab1 = QtGui.QWidget()
tab2 = QtGui.QWidget()
tab3 = QtGui.QWidget()
tab4 = QtGui.QWidget()
# Resize width and height
tabs.resize(1000, 1000)
# Set layout of first tab
vBoxlayout = QtGui.QVBoxLayout()
pushButton1 = QtGui.QPushButton("Start")
pushButton2 = QtGui.QPushButton("Settings")
pushButton3 = QtGui.QPushButton("Stop")
vBoxlayout.addWidget(pushButton1)
vBoxlayout.addWidget(pushButton2)
vBoxlayout.addWidget(pushButton3)
tab1.setLayout(vBoxlayout)
# Add tabs
tabs.addTab(tab1,"Tab 1")
tabs.addTab(tab2,"Tab 2")
tabs.addTab(tab3,"Tab 3")
tabs.addTab(tab4,"Tab 4")
# Set title and show
tabs.setWindowTitle('PyQt QTabWidget # pythonspot.com')
tabs.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I think there is several solution to achieve this.
I will give an example.
First of all, you need to create a main window, where you will create a layout with 2 frames (I took gridlayout for this example). Once done, you can now set one layout per frame, and add widget as you want in those layout. It will populate the frame. So you can populate one frame with your tabs, and one which stay fix.
I did not take in consideration size of the widget, you can fit them as you want.
I just modified a little your example :
import sys
from PyQt4 import QtGui, QtCore
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = QtGui.QWidget()
mainLayout = QtGui.QGridLayout(mainWindow)
frameLeft = QtGui.QFrame(mainWindow)
frameLeft.setFrameShape(QtGui.QFrame.StyledPanel)
frameLeft.setFrameShadow(QtGui.QFrame.Raised)
gridLayoutLeft = QtGui.QGridLayout(frameLeft)
mainLayout.addWidget(frameLeft, 0, 0, 1, 1, QtCore.Qt.AlignVCenter)
frameRigth = QtGui.QFrame(mainWindow)
frameRigth.setFrameShape(QtGui.QFrame.StyledPanel)
frameRigth.setFrameShadow(QtGui.QFrame.Raised)
gridLayoutRigth = QtGui.QGridLayout(frameRigth)
mainLayout.addWidget(frameRigth, 0, 1, 1, 1, QtCore.Qt.AlignVCenter)
tabs = QtGui.QTabWidget()
gridLayoutRigth.addWidget(tabs, 0, 0, 1, 1, QtCore.Qt.AlignVCenter)
button = QtGui.QPushButton('test')
gridLayoutLeft.addWidget(button, 0, 0, 1, 1, QtCore.Qt.AlignVCenter)
# Create tabs
tab1 = QtGui.QWidget()
tab2 = QtGui.QWidget()
tab3 = QtGui.QWidget()
tab4 = QtGui.QWidget()
# Set layout of first tab
vBoxlayout = QtGui.QVBoxLayout()
pushButton1 = QtGui.QPushButton("Start")
pushButton2 = QtGui.QPushButton("Settings")
pushButton3 = QtGui.QPushButton("Stop")
vBoxlayout.addWidget(pushButton1)
vBoxlayout.addWidget(pushButton2)
vBoxlayout.addWidget(pushButton3)
tab1.setLayout(vBoxlayout)
# Add tabs
tabs.addTab(tab1, "Tab 1")
tabs.addTab(tab2, "Tab 2")
tabs.addTab(tab3, "Tab 3")
tabs.addTab(tab4, "Tab 4")
# Set title and show
mainWindow.setWindowTitle('PyQt QTabWidget # pythonspot.com')
mainWindow.resize(mainWindow.sizeHint())
mainWindow.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I hope this help !
Related
I want to display in a QWidget windows 2 buttons and 2 label where the 2 buttons as on the same horizontal layout. and each button will have under it the label.
For this i use to library :
QHBoxLayout
QVBoxLayout
When i run the script it doesn't display all the created widgets.
it display 1 button and 1 label.
code:
import sys
from PyQt5 import QtWidgets
def basicWindow():
app = QtWidgets.QApplication(sys.argv)
windowExample = QtWidgets.QWidget()
buttonA = QtWidgets.QPushButton('Click!')
labelA = QtWidgets.QLabel('Label Example')
buttonb = QtWidgets.QPushButton('Click 2!')
labelb = QtWidgets.QLabel('Label Example 2')
v_box_H = QtWidgets.QHBoxLayout()
# v_box_H2 = QtWidgets.QHBoxLayout()
v_box = QtWidgets.QVBoxLayout()
v_box.addWidget(buttonA)
v_box.addWidget(labelA)
v_box2 = QtWidgets.QVBoxLayout()
v_box2.addWidget(buttonb)
v_box2.addWidget(labelb)
v_box_H.addLayout(v_box)
windowExample.setLayout(v_box)
windowExample.setLayout(v_box2)
windowExample.setWindowTitle('PyQt5 Lesson 4')
windowExample.show()
sys.exit(app.exec_())
basicWindow()
If you run the application from terminal / CMD you get the error:
QWidget::setLayout: Attempting to set QLayout "" on QWidget "", when the QLayout already has a parent
Try it:
import sys
from PyQt5 import QtWidgets
def basicWindow():
app = QtWidgets.QApplication(sys.argv)
windowExample = QtWidgets.QWidget()
buttonA = QtWidgets.QPushButton('Click!')
labelA = QtWidgets.QLabel('Label Example')
buttonb = QtWidgets.QPushButton('Click 2!')
labelb = QtWidgets.QLabel('Label Example 2')
v_box_H = QtWidgets.QHBoxLayout(windowExample) # + windowExample
# v_box_H2 = QtWidgets.QHBoxLayout()
v_box = QtWidgets.QVBoxLayout()
v_box.addWidget(buttonA)
v_box.addWidget(labelA)
v_box2 = QtWidgets.QVBoxLayout()
v_box2.addWidget(buttonb)
v_box2.addWidget(labelb)
v_box_H.addLayout(v_box)
v_box_H.addLayout(v_box2) # +++
# windowExample.setLayout(v_box) # -
# windowExample.setLayout(v_box2) # -
windowExample.setWindowTitle('PyQt5 Lesson 4')
windowExample.show()
sys.exit(app.exec_())
basicWindow()
Hello I have this code using python and pyqt5 which allows to display a graphical interface :
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setToolTip('Remove')
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner)
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
When I execute my code I get this :
whereas I would like to get something like this :
How can I do to do this ?
Thank you a lot !
QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopRightCorner)
Any previously set corner widget is hidden. https://doc.qt.io/qt-5/qtabwidget.html#setCornerWidget
Try it:
import sys
from PyQt5 import QtCore, QtWidgets
class TabPage(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
group = QtWidgets.QGroupBox('Monty Python')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(group)
grid = QtWidgets.QGridLayout(group)
grid.addWidget(QtWidgets.QLabel('Enter a name:'), 0, 0)
grid.addWidget(QtWidgets.QLabel('Choose a number:'), 0, 1)
grid.addWidget(QtWidgets.QLineEdit(), 1, 0)
grid.addWidget(QtWidgets.QComboBox(), 1, 1)
grid.addWidget(QtWidgets.QPushButton('Click Me!'), 1, 2)
grid.addWidget(QtWidgets.QSpinBox(), 2, 0)
grid.addWidget(QtWidgets.QPushButton('Clear Text'), 2, 2)
grid.addWidget(QtWidgets.QTextEdit(), 3, 0, 1, 3)
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.tabs = QtWidgets.QTabWidget()
self.tabs.setTabsClosable(True) # +
self.tabs.tabCloseRequested.connect(self.qtabwidget_tabcloserequested) # +
self.tabs.currentChanged.connect(lambda: print(f'currentIndex->{self.tabs.currentIndex()}')) #+
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setFixedSize(20, 20) # +
button.setToolTip('Add New Tab')
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_DialogYesButton))
# self.tabs.setCornerWidget(button, QtCore.Qt.TopRightCorner)
button1 = QtWidgets.QToolButton()
button1.setFixedSize(20, 20) # +
button1.setToolTip('Remove')
button1.clicked.connect(self.removeTab) # removeTab
button1.setIcon(self.style().standardIcon(
QtWidgets.QStyle.SP_BrowserStop))
# Any previously set corner widget is hidden.
# self.tabs.setCornerWidget(button1, QtCore.Qt.TopRightCorner) #
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
self.widget = QtWidgets.QWidget()
h_layout = QtWidgets.QHBoxLayout(self.widget)
h_layout.setContentsMargins(0, 0, 0, 0)
h_layout.addWidget(button)
h_layout.addWidget(button1)
self.tabs.setCornerWidget(self.widget, QtCore.Qt.TopRightCorner)
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
self.addNewTab()
def addNewTab(self):
text = 'Tab %d' % (self.tabs.count() + 1)
self.tabs.addTab(TabPage(self.tabs), text)
#QtCore.pyqtSlot(int)
def qtabwidget_tabcloserequested(self, index):
# gets the widget
widget = self.tabs.widget(index)
# if the widget exists
if widget:
# removes the widget
widget.deleteLater()
# removes the tab of the QTabWidget
self.tabs.removeTab(index)
def removeTab(self):
print('def removeTab(self): print')
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
sys.exit(app.exec_())
Since the other answer does not explain the cause of the problem and its code is confusing, I will explain the error in detail.
The error is that there can only be one cornerWidget, if you set a second cornerWidget it will replace the previous one, therefore only one QToolButton is observed. If you want to show several widgets then you have to use a container like a QWidget and place the other widgets there.
# ...
layout.addWidget(self.tabs)
button = QtWidgets.QToolButton()
button.setToolTip("Add New Tab")
button.clicked.connect(self.addNewTab)
button.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_DialogYesButton))
button1 = QtWidgets.QToolButton()
button1.setToolTip("Remove")
button1.clicked.connect(self.addNewTab)
button1.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_BrowserStop))
container = QtWidgets.QWidget()
container.setContentsMargins(0, 0, 0, 0)
lay = QtWidgets.QHBoxLayout(container)
lay.setContentsMargins(0, 0, 0, 0)
lay.addWidget(button)
lay.addWidget(button1)
self.tabs.setCornerWidget(container, QtCore.Qt.TopRightCorner)
self.addNewTab()
I want to adjust height and width using grid layout.
class MatchStepWidget(QWidget):
def __init__(self,parent):
super(MatchStepWidget,self).__init__(parent)
self.initUI()
def initUI(self):
layout = QGridLayout(self)
test = QTextEdit(self)
bt_test = QPushButton(self)
layout.addWidget(test,0,0,1,1)
layout.addWidget(bt_test,1,0,1,1)
# bt_test.hide()
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
myapp = MatchStepWidget(None)
myapp.show()
sys.exit(app.exec_())
I set row span and col span as same for both button and textedit.
but Textedit covers so much area than pushbutton
How can i adjust Textedit area using grid layout?
The default QPushButton will not stretch vertically, if you want to change it then you must modify the QSizePolicy. Also if you want the height of the QPushButton and the QTextEdit to be the same then you must set the stretch factor for each row of the layout:
def initUI(self):
test = QTextEdit()
bt_test = QPushButton()
layout = QGridLayout(self)
layout.addWidget(test, 0, 0)
layout.addWidget(bt_test, 1, 0)
sp = bt_test.sizePolicy()
sp.setVerticalPolicy(QSizePolicy.Minimum)
bt_test.setSizePolicy(sp)
layout.setRowStretch(0, 1)
layout.setRowStretch(1, 1)
So I'm trying to get a grip on Qt (more specifically, Pyqt), and I want to create a simple feedback form. It should have
a title
a name ('author')
a message
a send and a cancel button
Let's try without the buttons, first (the App class just provides a button to create a popup. the question concerns the Form class below it):
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QDesktopWidget,\
QHBoxLayout, QVBoxLayout, QGridLayout,\
QPushButton, QLabel,QLineEdit, QTextEdit,\
qApp
from PyQt5.QtGui import QIcon
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'PyQt5 Layout Demo'
self.popup = None
self.initUI()
def initUI(self):
self.setWindowTitle(self.title)
self.setWindowIcon(QIcon('imgs/python3.png'))
formButton = QPushButton("show form")
formButton.clicked.connect(self.showPopup)
formBox = QHBoxLayout()
formBox.addWidget(formButton)
formBox.addStretch(1)
vbox = QVBoxLayout()
vbox.addLayout(formBox)
vbox.addStretch(1)
# self.setLayout(vbox) # would work if this was a QWidget
# instead, define new central widget
window = QWidget()
window.setLayout(vbox)
self.setCentralWidget(window)
self.center(self)
self.show()
#staticmethod
def center(w: QWidget):
qr = w.frameGeometry() # get a rectangle for the entire window
# center point = center of screen resolution
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp) # move center of rectangle to cp
w.move(qr.topLeft()) # move top-left point of window to top-let point of rectangle
def showPopup(self):
if self.popup is None:
self.popup = Form(self)
self.popup.setGeometry(10, 10, 300, 400)
self.center(self.popup)
self.popup.show()
class Form(QWidget):
def __init__(self, main):
super().__init__()
self.initUI()
self.main = main
def initUI(self):
self.setWindowTitle('Feedback')
self.setWindowIcon(QIcon('imgs/python3.png'))
title = QLabel('Title')
author = QLabel('Author')
message = QLabel('Message')
titleEdit = QLineEdit()
authorEdit = QLineEdit()
messageEdit = 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(message, 3, 0)
grid.addWidget(messageEdit, 4, 0, 6, 0)
self.setLayout(grid)
# probably should delegate to self.main, but bear with me
def send(self):
self.main.popup = None
self.hide()
def cancel(self):
self.hide()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
Ok, looks about right. There's a bit too much spacing in-between the line edits and the text edit, but since I want to add some buttons below it, that should be be a problem.
So I add:
sendBtn = QPushButton("send")
cancelBtn = QPushButton("cancel")
sendBtn.clicked.connect(self.send)
cancelBtn.clicked.connect(self.cancel)
grid.addWidget(sendBtn, 7, 1)
grid.addWidget(cancelBtn, 7, 2)
which yields
Now obviously, I forgot to stretch the title and author line edits to the newly introduced column 2. Easy enough to fix but what really bothers me is the placement of the buttons.
WHY do they show up in the middle of the text edit? I can see how Qt chooses the column size, and why that would lead to the buttons' being of different size, but since the tutorial doesn't actually add buttons to the form, I have no idea how to fix that.
I could, of course, simply add boxes:
sendBtn = QPushButton("send")
cancelBtn = QPushButton("cancel")
sendBtn.clicked.connect(self.send)
cancelBtn.clicked.connect(self.cancel)
btns = QHBoxLayout()
btns.addStretch(1)
btns.addWidget(sendBtn)
btns.addWidget(cancelBtn)
l = QVBoxLayout()
l.addLayout(grid)
l.addLayout(btns)
self.setLayout(l)
With which the popup then actually starts looking closer to something acceptable:
But is there a way to fix this within the grid layout, instead?
You seem to have misunderstood the signature of addWidget. The second and third arguments specify the row and column that the widget is placed in, whilst the third and fourth specify the row-span and column-span.
In your example, the problems start here:
grid.addWidget(message, 3, 0)
grid.addWidget(messageEdit, 4, 0, 6, 0)
where you make the text-edit span six rows and zero columns - which I doubt is what you intended. Instead, you probably want this:
grid.addWidget(message, 3, 0, 1, 2)
grid.addWidget(messageEdit, 4, 0, 1, 2)
which will make the message label and text-edit span the two columns created by the title and author fields above.
Now when you add the buttons, they must have a layout of their own, since the top two rows are already determining the width of the two columns. If you added the buttons directly to the grid, they would be forced to have the same widths as the widgets in the top two rows (or vice versa). So the buttons should be added like this:
hbox = QHBoxLayout()
sendBtn = QPushButton("send")
cancelBtn = QPushButton("cancel")
sendBtn.clicked.connect(self.send)
cancelBtn.clicked.connect(self.cancel)
hbox.addStretch()
hbox.addWidget(sendBtn)
hbox.addWidget(cancelBtn)
grid.addLayout(hbox, 5, 0, 1, 2)
I am trying to add tab for 2 grid layout, but when I ran the code it seems like there is no tab.
I think
wid_inner.tab = QtGui.QTabWidget()
is not adding the tab correctly to the grid
import sys
from PySide import QtGui
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
# setting the outter widget and layout
wid = QtGui.QWidget()
grid = QtGui.QGridLayout()
wid.setLayout(grid)
# setting the inner widget and layout
grid_inner = QtGui.QGridLayout()
wid_inner = QtGui.QWidget()
wid_inner.setLayout(grid_inner)
# add the inner widget to the outer layout
grid.addWidget(wid_inner)
# add tab frame to widget
wid_inner.tab = QtGui.QTabWidget()
# create tab
new_tab = QtGui.QWidget()
grid_tab = QtGui.QGridLayout()
grid_tab.setSpacing(10)
new_tab.setLayout(grid_tab)
new_tab.tab_name_private = "test1"
wid_inner.tab.addTab(new_tab, "test1")
# create tab 2
new_tab2 = QtGui.QWidget()
new_tab2.setLayout(grid_tab)
wid_inner.tab.addTab(new_tab2, "test2")
wid.show()
sys.exit(app.exec_())
Any help would be appreciated thanks
You need to provide the parent to each inner widget, and the tab widget wid_inner.tab was not being added to any layout. This seems a little complicated to establish the layout... Have you considered using QtDesigner?
wid = QtGui.QWidget()
grid = QtGui.QGridLayout(wid)
wid.setLayout(grid)
# setting the inner widget and layout
grid_inner = QtGui.QGridLayout(wid)
wid_inner = QtGui.QWidget(wid)
wid_inner.setLayout(grid_inner)
# add the inner widget to the outer layout
grid.addWidget(wid_inner)
# add tab frame to widget
wid_inner.tab = QtGui.QTabWidget(wid_inner)
grid_inner.addWidget(wid_inner.tab)
# create tab
new_tab = QtGui.QWidget(wid_inner.tab)
grid_tab = QtGui.QGridLayout(new_tab)
grid_tab.setSpacing(10)
new_tab.setLayout(grid_tab)
new_tab.tab_name_private = "test1"
wid_inner.tab.addTab(new_tab, "test1")
# create tab 2
new_tab2 = QtGui.QWidget(wid_inner.tab)
new_tab2.setLayout(grid_tab)
wid_inner.tab.addTab(new_tab2, "test2")
wid.show()
app.exec_()