Vertical Layout not showing widgets [duplicate] - python

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)

Related

QStackedWidget opening multiple windows

I have build a small application using PyQt and as far as I know QStackedWidget is used for making multipage applications. The issue is that it is opening multiple pages and also I'm unable to redirect to class Xyz using button in class Abc.
main.py
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.stacked_widget = QtGui.QStackedWidget()
layout = QtGui.QVBoxLayout()
layout.setContentsMargins(0,0,0,0)
layout.addWidget(self.stacked_widget)
widget = QtGui.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
widget_1 = Abc(self.stacked_widget)
widget_2 = Xyz(self.stacked_widget)
self.stacked_widget.addWidget(widget_1)
self.stacked_widget.addWidget(widget_2)
self.showMaximized()
class Abc(QtGui.QWidget):
def __init__(self, stacked_widget, parent=None):
super(Abc, self).__init__(parent)
self.stacked_widget = stacked_widget
self.frame = QtGui.QFrame()
self.frame_layout = QtGui.QVBoxLayout()
self.button = QtGui.QPushButton('Click me!')
self.button.clicked.connect(self.click)
self.frame_layout.addWidget(self.button)
self.frame.setLayout(self.frame_layout)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.frame)
self.setLayout(self.layout)
self.showMaximized()
def click(self):
print("You clicked me!")
self.stacked_widget.setCurrentIndex(1)
class Xyz(QtGui.QWidget):
def __init__(self, parent=None):
super(Xyz, self).__init__(parent)
self.frame_layout = QtGui.QStackedLayout()
self.page1 = Page("Page 1", self.frame_layout)
self.page2 = Page("Page 2", self.frame_layout)
self.frame_layout.addWidget(self.page1)
self.frame_layout.addWidget(self.page2)
class Page(QtGui.QWidget):
def __init__(self, text, frame_layout, parent=None):
super(Page, self).__init__(parent)
print(self.width(), self.height())
self.frame_layout = frame_layout
self.frame = QtGui.QFrame()
self.frame_layout = QtGui.QVBoxLayout()
self.frame.setStyleSheet("background-color: rgb(191, 191, 191)")
self.label = QtGui.QLabel()
self.label.setText(text)
self.frame_layout.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
self.frame.setLayout(self.frame_layout)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.frame)
self.layout.setContentsMargins(0,0,0,0)
self.setLayout(self.layout)
self.showMaximized()
print(self.width(), self.height())
if __name__ == "__main__":
app = QtGui.QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
The main problem is that you are not setting the QStackedLayout for the Xyz widget, the result is that all pages will actually appear as top level windows.
When a widget is added to a layout, it takes ownership of it; if the layout is already set to a widget, then the widget that was added to the layout becomes reparented to the other. The same happens if you set the layout afterwards.
Why does it show the first page as a separate window? And why doesn't it show the second?
When a new widget is created without a parent, it becomes a top level window; when a widget is added to a new stacked layout, Qt automatically tries to show it; you didn't set the layout to anything (which would reparent it as explained before), and the result is that the first page is shown as a standalone window.
Now, since the first "screen" is going to be shown only the first time, you can set that widget as the central widget, and then set a Xyz instance (which is actually a QStackedWidget subclass) as a new central widget.
Note that you don't need to use QWidget to add a QFrame if that's the only parent widget shown: you can just subclass QFrame.
This is a much simpler and cleaner version of your code:
from PyQt4 import QtGui, QtCore
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.startPage = Abc()
self.setCentralWidget(self.startPage)
self.startPage.startRequest.connect(self.buildPages)
def buildPages(self):
self.pages = Xyz()
self.setCentralWidget(self.pages)
class Abc(QtGui.QFrame):
startRequest = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(Abc, self).__init__(parent)
layout = QtGui.QVBoxLayout(self)
self.button = QtGui.QPushButton('Click me!')
self.button.clicked.connect(self.startRequest)
layout.addWidget(self.button)
class Xyz(QtGui.QStackedWidget):
def __init__(self, parent=None):
super(Xyz, self).__init__(parent)
self.page1 = Page("Page 1")
self.page2 = Page("Page 2")
self.addWidget(self.page1)
self.addWidget(self.page2)
self.page1.switchRequest.connect(lambda: self.setCurrentIndex(1))
self.page2.switchRequest.connect(lambda: self.setCurrentIndex(0))
class Page(QtGui.QFrame):
switchRequest = QtCore.pyqtSignal()
def __init__(self, text, parent=None):
super(Page, self).__init__(parent)
layout = QtGui.QVBoxLayout(self)
# set the background only for QFrame subclasses (which also includes
# QLabel), this prevents setting the background for other classes,
# such as the push button
self.setStyleSheet('''
QFrame {
background-color: rgb(191, 191, 191)
}
''')
# when adding a widget to a layout, the layout tries to automatically
# make it as big as possible (based on the widget's sizeHint); so you
# should not use the alignment argument for layout.addWidget(), but for
# the label instead
self.label = QtGui.QLabel(text, alignment=QtCore.Qt.AlignCenter)
layout.addWidget(self.label)
self.switchButton = QtGui.QPushButton('Switch')
layout.addWidget(self.switchButton)
self.switchButton.clicked.connect(self.switchRequest)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Use a variable/data in two tabs in PyQt5 / Python

I am trying to get input from a user in one tab then show it in the second tab. I could not find a question or an example about how to do that. This is an example of the code that I want to use, How can I show the data from Tab(1) Qlabel in QTextEdit box in Tab(2), I am a beginner in pyqt5 and not sure how this would work:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TabWidget(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Tab Widget Application')
tabwidget = QTabWidget()
tabwidget.addTab(FirstTab(), 'First Tab')
tabwidget.addTab(SecondTab(),'Second Tab')
vbox=QVBoxLayout()
vbox.addWidget(tabwidget)
self.setLayout(vbox)
class FirstTab(QWidget):
def __init__(self):
super().__init__()
self.nameLabel = QLabel(self)
self.nameLabel.setText('Name:')
self.line = QLineEdit(self)
self.line.move(80, 20)
self.line.resize(200, 32)
self.nameLabel.move(20, 20)
self.btn=QPushButton('switch',self)
self.btn.move(80, 50)
self.btn.clicked.connect(lambda: SecondTab.display(SecondTab(),self.nameLabel.text()))
class SecondTab(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.editor=QTextEdit()
self.layout.addWidget(self.editor)
self.setLayout(self.layout)
def display(self,text):
self.editor.setText(text)
if __name__ == '__main__':
app=QApplication(sys.argv)
tabwidget = TabWidget()
tabwidget.resize(500,500)
tabwidget.show()
app.exec()
In order to track changes amongst child widgets, you'll need a main "controller".
Your QTabWidget will suffice, as long as you implement it in the correct way:
class TabWidget(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle('Tab Widget Application')
# if the target widget of the layout is provided as an init argument, the
# layout will be automatically set to it
vbox = QVBoxLayout(self)
tabwidget = QTabWidget()
vbox.addWidget(tabwidget)
firstTab = FirstTab()
tabwidget.addTab(firstTab, 'First Tab')
secondTab = SecondTab()
tabwidget.addTab(secondTab,'Second Tab')
firstTab.line.textChanged.connect(secondTab.editor.setPlainText)
firstTab.btn.clicked.connect(lambda: tabwidget.setCurrentWidget(secondTab))

Cannot create QScrollArea with QWidget and QVBoxLayout to QWidget with QVBoxLayout

I have this code:
class Window(QWidget):
def __init__(self):
super().__init__()
def init_gui(self):
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.new1()
self.new2()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
scroll_area.setWidget(widget)
layout = QVBoxLayout()
widget.setLayout(layout)
button = QPushButton("Ahoj")
layout.addWidget(button)
self.layout.addLayout(layout)
def new1(self):
self.create_scroll_area()
def new2(self):
self.create_scroll_area()
I get this error message:
QLayout::addChildLayout: layout "" already has a parent
What's wrong?
Who is layout's parent? Widget? I also tried self.widget instead of widget and it still does not work.
Please try this code.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent=None)
self.init_gui()
def init_gui(self):
self.create_scroll_area()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
layout = QVBoxLayout()
button = QPushButton("Ahoj")
layout.addWidget(button)
widget.setLayout(layout)
scroll_area.setWidget(widget)
self.setLayout(layout)
def main():
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Let's revise from A to Z.
1 to write self.init_gui() in __init__ constructor.
If you don't do it, you can't execute init_gui method at the first time.
2.setLayout() or setWidget() should be written at the last place at least.
In python, we prepare the things we want to show, and set them on the mainwidget, and show them at the last time.
3.please pay attention to self.layout name.
Widget has originally setLayout() method. and layout() method.
If you make self.layout = ***, you crush the original method of QWidget.
4. it may as well delete new1 and new2 method.
please call them directly.
5. please look create_scroll_area method.
You make three widget.QScrollArea,QWidget,QPushButton.
and make a layout object.and you set the layout into QWidget.
But you set the QWidget before the widget set the layout.
It is not good order for coding.
You make QPushButton but the button doesn't belong to any widget.
Because you set the button on the self.layout certainly, but if you want to show it, you must setLayout(self.layout) at the last position.

PyQt: switch windows/layouts created by Qt Designer

I am creating an app using PyQt4. I have created two interfaces with Qt Designer. When a button is pushed I would like to switch between one layout and the other.
A sample of my code is:
from PyQt4 import QtGui, uic
form_class = uic.loadUiType("sample.ui")[0]
form_class2 = uic.loadUiType("sample2.ui")[0]
class SecondLayout(form_class2, QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
form_class2.setupUi(self)
class MainWindow(form_class, QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self, parent)
self.setupUi(self)
self.btn.clicked.connect(self.open_new_window)
def open_new_window(self):
self.Window = SecondLayout()
# here I would like to switch the layout with a layout of self.Window
app = QtGui.QApplication(sys.argv)
myWindow = MainWindow(None)
myWindow.show()
app.exec_()
I have done a lot of searching and reading about QStackedLayout, but haven't been able to get it to work with layouts created in Qt Designer.
My Question is how would I be able to have one Main Window and switch its central widget but i'm not sure if that would work for the seperate menus. I have defined all the menus and widgets and status bars, etc. in Qt Designer as two different projects(both main windows) so I would like to have the main program in one of the main windows, then at some point create an instance of the second main window and switch the layout and all the widgets, menus, text edits, etc. I tried using setCentralWidget but hasn't worked for me.
Could someone please explain to me how to do this.
It sounds like you have two completely separate main windows. There is really no point in switching all the widgets, menus, toolbars, etc, because they will have no shared code. You might just as well simply hide one window, and then show the other one.
Here is a simple demo that shows one way to do that:
PyQt5
from PyQt5 import QtWidgets
class Window1(QtWidgets.QMainWindow):
def __init__(self, window2=None):
super(Window1, self).__init__()
self.setGeometry(500, 100, 100, 50)
self.button = QtWidgets.QPushButton('Go To Window 2', self)
self.button.clicked.connect(self.handleButton)
self.setCentralWidget(self.button)
self._window2 = window2
def handleButton(self):
self.hide()
if self._window2 is None:
self._window2 = Window2(self)
self._window2.show()
class Window2(QtWidgets.QMainWindow):
def __init__(self, window1=None):
super(Window2, self).__init__()
self.setGeometry(500, 100, 100, 50)
self.button = QtWidgets.QPushButton('Go To Window 1', self)
self.button.clicked.connect(self.handleButton)
self.setCentralWidget(self.button)
self._window1 = window1
def handleButton(self):
self.hide()
if self._window1 is None:
self._window1 = Window1(self)
self._window1.show()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = Window1()
window.show()
sys.exit(app.exec_())
PyQt4
from PyQt4 import QtCore, QtGui
class Window1(QtGui.QMainWindow):
def __init__(self, window2=None):
super(Window1, self).__init__()
self.setGeometry(500, 100, 100, 50)
self.button = QtGui.QPushButton('Go To Window 2', self)
self.button.clicked.connect(self.handleButton)
self.setCentralWidget(self.button)
self._window2 = window2
def handleButton(self):
self.hide()
if self._window2 is None:
self._window2 = Window2(self)
self._window2.show()
class Window2(QtGui.QMainWindow):
def __init__(self, window1=None):
super(Window2, self).__init__()
self.setGeometry(500, 100, 100, 50)
self.button = QtGui.QPushButton('Go To Window 1', self)
self.button.clicked.connect(self.handleButton)
self.setCentralWidget(self.button)
self._window1 = window1
def handleButton(self):
self.hide()
if self._window1 is None:
self._window1 = Window1(self)
self._window1.show()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window1()
window.show()
sys.exit(app.exec_())

PyQt : How to set up a widget hidding an other widget if it's visible?

Let's say I created two QObject in my interface (ui). I would like to connect these two widgets and let them controling each other depending on their visual status. If one is hidden, the other one must be visible. And vice versa.
Can you help me ? :)
Thanks !
Nico
Possible solution: Sublclass widgets and override hideEvent and showEvent:
#!/usr/bin/env python
import sys
from PyQt4 import QtCore, QtGui
class CustomWidget(QtGui.QLabel):
signal_hided = QtCore.pyqtSignal()
signal_shown = QtCore.pyqtSignal()
def hideEvent(self, event):
print 'hideEvent'
super(CustomWidget, self).hideEvent(event)
self.signal_hided.emit()
def showEvent(self, event):
print 'showEvent'
super(CustomWidget, self).showEvent(event)
self.signal_shown.emit()
class MainWidget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.widget1 = CustomWidget('Widget1')
self.widget2 = CustomWidget('Widget2')
# connect signals, so if one widget is hidden then other is shown
self.widget1.signal_hided.connect(self.widget2.show)
self.widget2.signal_hided.connect(self.widget1.show)
self.widget2.signal_shown.connect(self.widget1.hide)
self.widget1.signal_shown.connect(self.widget2.hide)
# some test code
self.button = QtGui.QPushButton('test')
layout = QtGui.QVBoxLayout()
layout.addWidget(self.button)
layout.addWidget(self.widget1)
layout.addWidget(self.widget2)
self.setLayout(layout)
self.button.clicked.connect(self.do_test)
def do_test(self):
if self.widget1.isHidden():
self.widget1.show()
else:
self.widget2.show()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
widget = MainWidget()
widget.resize(640, 480)
widget.show()
sys.exit(app.exec_())

Categories

Resources