I am trying to create an application window with PyQt4. I want to create a window with a frame and inside that frame some widgets such as labels and text editors.
I created the frame as a QGroupBox to be able to put a title on it.
I know that HBox and VBox seem to be the prefered layout when you deal with frames, however, I would like to manage the positionning of the widgets inside my frame with a grid Layout, which I find easier to manage.
So I tried this piece of code :
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout()
grid = QtGui.QGridLayout()
#Definition des Tracing Parameters widgets
WindowSize = QtGui.QLabel("Window size (m)")
SampPts = QtGui.QLabel("Sampling points")
WindowSizeEdit = QtGui.QLineEdit()
SampPtsEdit = QtGui.QLineEdit()
TracParamFrame = QtGui.QGroupBox(self)
TracParamFrame.setTitle("Tracing Parameters")
hbox.addLayout(grid)
grid.addWidget(WindowSize,0,0)
grid.addWidget(WindowSizeEdit,0,1)
grid.addWidget(SampPts,1,0)
grid.addWidget(SampPtsEdit,1,1)
self.setLayout(hbox)
self.setGeometry(300,300,350,300)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
The main idea here was to create an hbox where I put the QGroupBox and then place a grid layout inside.
The problem is that in the application generated, the widgets are placed outside the frame, and in addition I get the error :
QLayout: Attempting to add QLayout "" to Example "", which already has a layout
QWidget::setLayout: Attempting to set QLayout "" on Example "", which already has a layout
I modified your code, by adding this statement: TracParamFrame.setLayout(hbox)
The code with this added is as:
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
hbox = QtGui.QHBoxLayout()
grid = QtGui.QGridLayout()
#Definition des Tracing Parameters widgets
WindowSize = QtGui.QLabel("Window size (m)")
SampPts = QtGui.QLabel("Sampling points")
WindowSizeEdit = QtGui.QLineEdit()
SampPtsEdit = QtGui.QLineEdit()
TracParamFrame = QtGui.QGroupBox(self)
TracParamFrame.setTitle("Tracing Parameters")
hbox.addLayout(grid)
grid.addWidget(WindowSize,0,0)
grid.addWidget(WindowSizeEdit,0,1)
grid.addWidget(SampPts,1,0)
grid.addWidget(SampPtsEdit,1,1)
TracParamFrame.setLayout(hbox)
#self.setLayout(hbox)
self.setGeometry(300,300,350,300)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Ok forget it, I found the solution. I had to use the setLayout method of the GroupBox as follows :
TracParamFrame.setLayout(grid)
Related
Probably a noob question, but I'm still learning PySide. So I'm trying to use QMainWindow which has a QFrame and the QFrame has two labels. I'm using QBoxLayouts on QMainWindow and QFrame. The problem is that when I set the QFrame to something like 200x200 then QMainWindow does not resize, it remains too small to display both labels. Correct me if I'm wrong but shouldn't QMainWindow automatically have the right size when using layouts? Additionaly when I output frame.sizeHint() then it outputs PySide.QtCore.QSize(97, 50) but I would expect it to be 200, 200.
The code below will reproduce the problem:
import sys
from PySide import QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
def initUI(self):
#-------
#CREATE WIDGETS
#-------
frame = QtGui.QFrame()
frame.setStyleSheet("QFrame {background-color: yellow}")
frame.setGeometry(0, 0, 200, 200)
someLabel = QtGui.QLabel("SomeLabel")
someOtherLabel = QtGui.QLabel("SomeOtherLabel")
self.setCentralWidget(frame)
#--------
#CREATE LAYOUT
#--------
frameLayout = QtGui.QVBoxLayout()
frameLayout.addWidget(someLabel)
frameLayout.addWidget(someOtherLabel)
frame.setLayout(frameLayout)
mainLayout = QtGui.QVBoxLayout()
mainLayout.addWidget(frame)
self.setLayout(mainLayout)
self.show()
def main():
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This is what happens after code is run:
A QMainWindow already has a top-level layout, so you should never set one yourself. All you need to do is set the central-widget, and then add a layout and widgets to that.
Your example can therefore be fixed like this:
frame.setLayout(frameLayout)
# get rid of these three lines
# mainLayout = QtGui.QVBoxLayout()
# mainLayout.addWidget(frame)
# self.setLayout(mainLayout)
self.show()
It's worth noting that there is possibly a bug/misfeature in PySide regarding this, because in PyQt your original script would print a useful error message:
QWidget::setLayout: Attempting to set QLayout "" on MainWindow "", which already has a layout
I am making a GUI with PyQt, and I am having issues with my MainWindow class. The window doesn't show widgets that I define in other classes, or it will show a small portion of the widgets in the top left corner, then cut off the rest of the widget.
Can someone please help me with this issue?
Here is some example code showing my issue.
import sys
from PyQt4 import QtGui, QtCore
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.resize(300, 400)
self.centralWidget = QtGui.QWidget(self)
self.hbox = QtGui.QHBoxLayout(self.centralWidget)
self.setLayout(self.hbox)
names = ['button1', 'button2', 'button3']
testButtons = buttonFactory(names, parent=self)
self.hbox.addWidget(testButtons)
class buttonFactory(QtGui.QWidget):
def __init__(self, names, parent=None):
super(buttonFactory, self).__init__(parent=parent)
self.vbox = QtGui.QVBoxLayout()
self.setLayout(self.vbox)
for name in names:
btn = QtGui.QPushButton(name)
self.vbox.addWidget(btn)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
gui = MainWindow()
gui.show()
app.exec_()
A QMainWindow has a central widget that is a container in which you should add your widgets. It has its own layout. The layout of the QMainWindow is for toolbars and such. The centralWidget must be set with the setCentralWidget method. It isn't enough to just call it self.centralWidget
Use the following three lines instead.
self.setCentralWidget(QtGui.QWidget(self))
self.hbox = QtGui.QHBoxLayout()
self.centralWidget().setLayout(self.hbox)
I found some code on here that shows an example of how you can get the window to resize when the widget is hidden, and it works for me. Here is the code:
from PyQt4 import QtCore, QtGui
import sys
class MainWindow(QtGui.QWidget):
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def startup(self):
self.show()
sys.exit(self.app.exec_())
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
myApp = MainWindow()
myApp.startup()
I then tried to modify this to match my existing code by separating the mainWindow class and the widget class. Here is the code that does that.
from PySide import QtGui,QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.setCentralWidget(self.w)
self.show()
class testW(QtGui.QWidget):
def __init__(self,parent):
super(testW,self).__init__()
self.parent = parent
self.button = QtGui.QPushButton('Show/Hide')
self.button.setCheckable(True)
self.button.setChecked(True);
self.frame = QtGui.QFrame()
self.frame.setFixedHeight(100)
self.layout = layout = QtGui.QVBoxLayout()
layout2 = QtGui.QVBoxLayout()
self.setLayout(layout)
self.frame.setLayout(layout2)
layout.addWidget(self.button)
layout.addWidget(self.frame)
layout.addStretch(1)
layout2.addWidget(QtGui.QLabel('Yoyoyo'))
self.button.toggled.connect(self.clickAction)
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.resizeMe)
def resizeMe(self):
self.resize(self.minimumSizeHint())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myApp = MainWindow()
sys.exit(app.exec_())
#time.sleep(1)
Running the first code does what I want it to. After I hide the widget, the window resizes to the correct size. The second implementation of the code does not shrink and expand the window when I hide and show the widget. Is this because the MainWindow is in a separate class?
Use size policies for your widgets. For your example you can change UI creation code as follows:
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.w = testW(self)
self.w.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding
)
self.setCentralWidget(self.w)
self.show()
Please note new setSizePolicy call which say Qt layout engine how to change the size of your widget according to its content.
Unfortunately QMainWindow does not respect sizeHint automatically, but it is calculated properly, so you can adjustSize manually:
def clickAction(self):
checked = self.button.isChecked()
if checked:
self.frame.show()
else:
self.frame.hide()
QtCore.QTimer.singleShot(0, self.parent.adjustSize)
You do not need to resize your widget itself, because it will be resized according to the policy. Even sizeHint will be calculated automatically so you need only to call adjustSize of QMainWindow.
PS: I used PySide2 instead of PySide so the imports are different a little bit:
from PySide2 import QtWidgets, QtCore
I am a newbie with PyQt. I am trying to organize my buttons on a grid layout, but I guess the window has a default layout already. How can I get rid of it and replace it with the new Grid Layout? I have contained the code block relevant with hashes ###, Here is my program:
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QWidget
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QWidget.__init__(self)
self.setMinimumSize (800,600) # set minimum size for window
self.setWindowTitle("CoolPlay Kabul") # set window title
self.setWindowIcon(QtGui.QIcon("images/CoolPlay.png"))# set icon for Window
myMenu = self.menuBar()
File_Menu = myMenu.addMenu("&File")
Items_Menu = myMenu.addMenu("&Items")
Playlist_Menu = myMenu.addMenu("&Playlist")
Option_Menu = myMenu.addMenu("&Option")
Exit_Menu = myMenu.addMenu("&Exit")
File_Menu.addAction("New Time")
File_Menu.addAction("Delete Time")
File_Menu.addSeparator()
File_Menu.addAction("Exit")
Items_Menu.addAction("New Item")
Items_Menu.addAction("Delete Item")
Items_Menu.addSeparator()
Items_Menu.addAction("Toggle Segue")
Playlist_Menu.addAction("Clear Playlist")
Playlist_Menu.addAction("Save playlist")
Playlist_Menu.addAction("Load Playlist")
Playlist_Menu.addSeparator()
Playlist_Menu.addAction("Clear 'Played' Indication")
Option_Menu.addAction("Application Setup")
Exit_Menu.addAction("Help")
Exit_Menu.addAction("About")
######################################################
Overall_Layout = QtGui.QGridLayout(self)
self.setLayout(Overall_Layout)
Play_Button = QtGui.QPushButton(QtGui.QIcon("images/PLAY.bmp"), "PLAY",self)
Overall_Layout.addWidget(Play_Button,1,2)
Overall_Layout.addWidget(Play_Button,2,2)
########################################################
self.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
CoolPlay = MainWindow()
CoolPlay.show()
sys.exit(app.exec_())
QMainWindow is a special widget since it already has a preset layout as shown below:
So in this case you should not set a layout to the QMainWindow but to the central widget, but first establish a centralwidget, using the indicated thing we get the following:
######################################################
central_widget = QtGui.QWidget()
self.setCentralWidget(central_widget)
Overall_Layout = QtGui.QGridLayout(central_widget)
Play_Button = QtGui.QPushButton(QtGui.QIcon("images/PLAY.bmp"), "PLAY")
Overall_Layout.addWidget(Play_Button,1,2)
Overall_Layout.addWidget(Play_Button,2,2)
########################################################
On the other hand if you inherit from QMainWindow you must call the QMainWindow constructor, but in code you call QWidget, so you must modify it to:
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
Or
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
I have to align 'Menu5' to the right side of MenuBar.
Is that possible in Python? (PyQt4)
Example
I found information on how to do this in C there
Aligning QMenuBar items (add some on left and some on right side)
But I don't know how I can do this in Python.
My code:
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class Example(QtGui.QMainWindow):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
grid = QtGui.QGridLayout()
panel = QtGui.QWidget()
panel.setLayout(grid)
self.setCentralWidget(panel)
menubar1 = self.menuBar()
menubar1.addMenu('&Menu1')
menubar1.addMenu('&Menu2')
menubar1.addMenu('&Menu3')
menubar1.addMenu('&Menu4')
menubar1.addMenu('&Menu5')
self.setLayout(grid)
self.move(300, 150)
self.setWindowTitle('TestApp')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Please help.
QMenuBar has setCornerWidget function which allows you to do that.
menubar1 = self.menuBar()
menubar1.addMenu('&Menu1')
menubar1.addMenu('&Menu2')
menubar1.addMenu('&Menu3')
menubar1.addMenu('&Menu4')
self.menuBr= QtGui.QMenuBar(menubar1)
menubar1.setCornerWidget(self.menuBr, QtCore.Qt.TopRightCorner)
self.menu5 = QtGui.QMenu(self.menuBr)
self.menu5.setTitle("Menu5")
self.menuBr.addAction(self.menu5.menuAction())