I would like to resize the MainWindow (QMainWindow) after I make some widgets unvisible and vice versa. And I want to block the window resize.
def hideAndShowWidget(self):
self.widgetObject.setVisible(not self.widgetObject.isVisible() )
# change main window size here
# ...
self.setFixedSize(self.width(), self.height())
My problem is, that i can not change the window size after i call setFixedSize() first time. I read here that I must use QWIDGETSIZE_MAX() to remove constraints, but I don't know how can I use it, I get the error:
NameError: name 'QWIDGETSIZE_MAX' is not defined
I think you have the mechanism more or less right. You just have to make sure the height calculation is done correctly (i.e. before the visibility of the widget changes).
The following example works correctly for me (only tested on Linux, though):
from PySide import QtGui
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.widgetObject = QtGui.QTextEdit(self)
self.button = QtGui.QPushButton('Hide Widget', self)
self.button.clicked.connect(self.hideAndShowWidget)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.widgetObject)
self.setFixedSize(300, 200)
def hideAndShowWidget(self):
height = self.height()
if self.widgetObject.isVisible():
height -= self.widgetObject.height()
self.widgetObject.setVisible(False)
self.button.setText('Show Widget')
else:
height += self.widgetObject.height()
self.widgetObject.setVisible(True)
self.button.setText('Hide Widget')
self.setFixedSize(self.width(), height)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Use the sizeHint(). It contains the size the widget would like to have. Set the fixed size exactly to the size hint.
Working example:
from PySide import QtGui
class Window(QtGui.QMainWindow):
def __init__(self):
super().__init__()
self.setFixedSize(400, 300)
widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout(widget)
button = QtGui.QPushButton('Toggle visibility')
button.clicked.connect(self.hideAndShowWidget)
layout.addWidget(button)
self.widgetObject = QtGui.QLabel('Test')
layout.addWidget(self.widgetObject)
self.setCentralWidget(widget)
def hideAndShowWidget(self):
self.widgetObject.setVisible(not self.widgetObject.isVisible() )
# change main window size
self.setFixedSize(self.sizeHint())
app = QtGui.QApplication([])
w = Window()
w.show()
app.exec_()
Related
I've just starting to learn/use PyQt for my internship and am having some issues finding out how to add a scroll bar to this simple program:
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(300,300,180,100)
self.button1= QPushButton(self)
self.button1.setText("Button 1")
self.button1.move(10,10)
self.button2= QPushButton(self)
self.button2.setText("Button 2")
self.button2.move(150,10)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
Essentially, the window has a set size (here it's 180x100) but has elements outside of that size (i.e. button2 extents from 150 to 220 which makes it half outside of the 180px window)
Click-dragging the window larger shows the entirety of button2, which is fine, but I need a way to keep the window the size it is and just have a scroll bar to see all of the unseen items.
The QScrollArea class provides a scrolling view onto another widget.
More... https://doc.qt.io/qt-5/qscrollarea.html
A scroll area is used to display the contents of a child widget within a frame.
If the widget exceeds the size of the frame, the view can provide scroll bars so
that the entire area of the child widget can be viewed.
The child widget must be specified with setWidget().
import sys
from PyQt5.Qt import *
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.setGeometry(300, 300, 180, 100)
self.scroll = QScrollArea()
self.widget = QWidget()
self.widget.resize(280, 200)
self.scroll.setWidget(self.widget)
self.button1= QPushButton(self.widget)
self.button1.setText("Button 1")
self.button1.move(10, 10)
self.button2= QPushButton(self.widget)
self.button2.setText("Button 2")
self.button2.move(150, 10)
self.setCentralWidget(self.scroll)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I'm trying to make a toolbox widget that will do various different things. But I'm having trouble with the layout management regarding the QScrollArea. Following the stripped version of the code I have:
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
scrollArea = QtWidgets.QScrollArea(self)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
group_box.setLayout(layout)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())
But this only gives me a small fixed subsection of the widget that scrolled. But what I really wants is the whole widget to be scrollable if the widget/window is smaller than the total size of all the group boxes. I.e I'd like the widget to be used as if it was all fixed width, but if the user resized the widget smaller than that, it would scroll appropriately. I've tried various different methods with no good results so now I'm deferring to those who have more experience with layout management than I. Thank you for your time.
You have to set the scrollArea to MyScrollWidget using a layout.
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
lay = QtWidgets.QVBoxLayout(self)
scrollArea = QtWidgets.QScrollArea()
lay.addWidget(scrollArea)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())
I have the following window with frames.
I want frame to be highlighted (in my case change its shape) when mouse is in its area.
from PyQt4 import QtGui, QtCore
import sys
app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window_layout = QtGui.QVBoxLayout()
window.setLayout(window_layout)
#fill content
for i in range(10):
label = QtGui.QLabel(str(i))
frame = QtGui.QFrame()
frame_layout = QtGui.QVBoxLayout()
frame.setLayout(frame_layout)
frame_layout.addWidget(label)
window_layout.addWidget(frame)
def layout_widgets(layout):
return (layout.itemAt(i) for i in range(layout.count()))
def mouse_enter(event):
print 'frame enter'
w.widget().setFrameShape(3)
def mouse_leave(event):
print 'frame leave'
w.widget().setFrameShape(0)
for w in layout_widgets(window_layout):
print w.widget()
w.widget().enterEvent = mouse_enter
w.widget().leaveEvent = mouse_leave
window.show()
sys.exit(app.exec_())
It works but only the last frame in layout highlights.
How to make only that frame change its shape where the mouse is?
I've tried the following:
def mouse_enter(event, frame):
print 'frame enter'
frame.setFrameShape(3)
w.widget().enterEvent = functools.partial(mouse_enter, w.widget())
but it gives an error. I have found one more way to do that - signal mapper
but I have no idea how to use it.
The problem in your code the variable w when executing the for is left with the last element, so it will only be executed in the latter. To solve this I have implemented a Frame class that inherits from QFrame where I overwrite the enterEvent and leaveEvent functions.
from PyQt4 import QtGui, QtCore
import sys
class Frame(QtGui.QFrame):
def __init__(self, text, parent=None):
super(Frame, self).__init__(parent=parent)
label = QtGui.QLabel(text)
frame_layout = QtGui.QVBoxLayout()
frame_layout.addWidget(label)
self.setLayout(frame_layout)
def enterEvent(self, event):
self.setFrameShape(3)
def leaveEvent(self, event):
self.setFrameShape(0)
app = QtGui.QApplication(sys.argv)
window = QtGui.QWidget()
window_layout = QtGui.QVBoxLayout()
window.setLayout(window_layout)
for i in range(10):
frame = Frame(str(i))
window_layout.addWidget(frame)
window.show()
sys.exit(app.exec_())
I want to display welcome label in middle of frame, how can I do that? It seems like layout problem as I googled but I haven't got final solution.
Here is the code:
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
class Window(QMainWindow):
def __init__(self):
super(Window, self).__init__()
palette = QPalette()
palette.setBrush(QPalette.Background, QBrush(QPixmap("Login page.jpg")))
self.setPalette(palette)
self.setWindowTitle("Login Frame")
self.setWindowIcon(QIcon('logo.png'))
self.setGeometry(50, 50, 500, 300)
self.setFixedSize(500, 300)
self.addWidgets()
def addWidgets(self):
self.lblWelcome = QLabel("Welcome to Railway e-Ticketing System", self)
self.lblWelcome.move(100,30)
wcFont = QFont("Open Sans", 25)
self.lblWelcome.setFont(wcFont)
self.lblUid = QLabel("User ID:", self)
self.lblUid.move(100,80)
font = QFont("Open Sans", 10)
self.lneUid = QLineEdit(self)
self.lneUid.setFont(font)
self.lneUid.setFixedHeight(25)
self.lneUid.setFixedWidth(200)
self.lneUid.move(225, 80)
self.lblPass = QLabel("Password:", self)
self.lblPass.move(100, 130)
self.lnePass = QLineEdit(self)
self.lnePass.setEchoMode(QLineEdit.Password)
self.lnePass.setFixedHeight(25)
self.lnePass.setFixedWidth(200)
self.lnePass.move(225, 130)
self.lblInvalid = QLabel("",self)
self.lblInvalid.move(100, 180)
self.btnLogin = QPushButton("Login",self)
#btnLogin.resize(btnLogin.sizeHint())
self.btnLogin.move(175, 230)
self.btnLogin.clicked.connect(self.authenticate)
#self.authenticate()
self.btnReg = QPushButton("Register", self)
self.btnReg.move(300, 230)
#btnReg.clicked.connect(register)
self.show()
def authenticate(self):
uid = self.lneUid.text()
upass = self.lnePass.text()
if(len(uid.strip()) == 0 or len(upass.strip()) == 0):
palette = QPalette()
palette.setColor(QPalette.Foreground, Qt.darkRed)
self.lblInvalid.setPalette(palette)
self.lblInvalid.setText("*Invalid credentials .")
else:
self.lblInvalid.setText("")
def main():
app = QApplication(sys.argv)
LoginWin = Window()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
And here is the output:
You are using a QMainWindow which already has a layout with a central widget, a toolbar, a menu bar etc. The right way to use it is to define a central Widget, and put all your label and buttons in it. You didn't, so your label is not displayed properly.
But for your login frame, you clearly don't need all of this. You just need a QWidget:
import sys
from PyQt4 import QtCore,QtGui
class LoginFrame(QtGui.QWidget):
def __init__(self):
super(LoginFrame, self).__init__()
...
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
win = LoginFrame()
win.show()
sys.exit(app.exec_())
Your code should work with a QWidget, but I would still advise reading about box layout. Right now, you're using absolute positioning, which means you have to manually place your widget at a precise position, and you can't resize your window.
A box layout would be more flexible, and practical. For example you can use QFormLayout for the userID and password.
More about layouts on the ZetCode tutorial
I am trying to set up a window that has a text input & a combo box. At the moment I just want to see the text & the selection displayed under the appropriate widget.
I have used QVBoxLayout() as I will be adding more stuff later & thought it would be a simple way of laying out the window.
Unfortunately only the combo box ever gets displayed. The code:
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
vbox = QtGui.QVBoxLayout()
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
So whats going on here? Am I missing some important directive to QVBoxLayout()?
Using Python 2.7 on Win 7 x64 machine with PyQt 4.
EDIT: Additional problem (still related to missing widgets)
I have amended the code following the clarification below. I then added more widgets when a certain option in the combobox is chosen (see below) but these widgets dont show. I attempted to add a child widget to 'widget' called 'ranPolyWidget' to take a numerical input.
# Combo box
def onActivated(self, text):
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
ranPolyWidget = QtGui.QWidget(self.widget)
rbox = QtGui.QVBoxLayout(ranPolyWidget)
rbox.addWidget(randomSeed)
self.layout().addWidget(ranPolyWidget)
self.show()
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
Same issue as before, no widgets. I am missing something pretty fundamental arent I?
You're forgetting to set it to the widget or main window, so since the QComboBox is the last one made, it's the only one displayed. Basically, everything is added to the layout, but the layout is "free-floating", and so it does not display properly. You need to bind the layout to a QWidget, which I do here. For most widgets, you can can do this by the QtGui.QVBoxLayout(widget) or by widget.setLayout(layout).
Alternatively, if you want multiple layouts on a widget, you can do have a parent layout and then add each child layout to the main layout.
EDIT: This is a better answer:
Make a widget, set layout to widget and set as central widget.
QMainWindow-s don't like you using the builtin layout or overriding it.
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
Old Answer:
self.layout().addLayout(vbox).
This should fix your issue:
Changes I made:
Since QMainWindow already has a layout, add in a widget (28G) and then set the VBoxLayout to the widget and add it to the main window.
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.layout().addWidget(widget)
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
EDIT:
For adding new widgets, you should add them to the layout of the central widget and parent them to that widget.
Here's how I'd restructure your full code:
from PyQt4 import QtCore, QtGui
import sys
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(polyType)
self.layout.addWidget(self.defaultPolyType)
self.layout.addStretch()
def onActivated(self, text):
'''Adds randSeed to layout'''
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
self.layout.addWidget(randomSeed)
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
# I like having class attributes bound in __init__
# Not very Qt of me, but it's more
# so I break everything down into subclasses
# find it more Pythonic and easier to debug: parent->child
# is easy when I need to repaint or delete child widgets
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):
'''Pass to child'''
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()