PyQt: switch windows/layouts created by Qt Designer - python

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_())

Related

Access A Variable from another class in PyQt5

In PyQt5, I've wrote a GUI. Basically, when a button is pressed, it open a new window, where choose an item from a list. What I want is that after you close the new window, the item you chose appears as text on the first window. It's a hard to explain.
This is the code:
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
from PyQt5 import QtWidgets
from PyQt5 import QtCore
from PyQt5 import QtGui
class Add(QtWidgets.QMainWindow):
def __init__(self):
super(Add, self).__init__()
#Set The UI
self.initUI()
#Set The GUI Position And Size
self.setGeometry(1050, 500, 400, 50)
#Set The GUI Title
self.setWindowTitle("Add")
def initUI(self):
Central = QtWidgets.QWidget(self)
self.setCentralWidget(Central)
self.deckButton = QtWidgets.QPushButton(self)
self.deckButton.setText("Choose")
self.deckButton.clicked.connect(self.open_deck_browser)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.deckButton, 1)
Central.setLayout(hbox)
def open_deck_browser(self):
self.w = SetDeck()
self.w.show()
class SetDeck(QtWidgets.QMainWindow):
def __init__(self):
super(SetDeck, self).__init__()
#Set The UI
self.initUI()
#Set The GUI Position And Size
self.setGeometry(200, 200, 800, 640)
#Set The GUI Title
self.setWindowTitle("Choose Deck")
def initUI(self):
widAddToDeckWindow = QtWidgets.QWidget(self)
self.setCentralWidget(widAddToDeckWindow)
#Create The List Widget
self.deckList = QtWidgets.QListWidget()
self.deckList.insertItem(0, "Hello")
self.deckList.insertItem(1, "Hi")
self.deckList.insertItem(2, "Hello There")
self.deckList.item(0).setSelected(True)
self.deckList.itemSelectionChanged.connect(self.show_List)
print([x.row() for x in self.deckList.selectedIndexes()])
#Create The Select Deck Button
self.selectDeck = QtWidgets.QPushButton(self)
self.selectDeck.setText("Choose")
hboxCreateBottomButtons = QtWidgets.QHBoxLayout()
hboxCreateBottomButtons.addStretch()
hboxCreateBottomButtons.addWidget(self.selectDeck)
#Create The Main VBox
vboxMain = QtWidgets.QVBoxLayout()
vboxMain.addWidget(self.deckList)
vboxMain.addLayout(hboxCreateBottomButtons)
widAddToDeckWindow.setLayout(vboxMain)
def show_List(self):
print(repr(self.deckList.selectedItems()[0].text()))
def window():
app = QtWidgets.QApplication(sys.argv)
win = Add()
win.show()
sys.exit(app.exec_())
window()
I've tried using global variables, but it didn't work.
First of all, I recommend you improve your style when naming variables as they make reading easier, for example that the class name are nouns and not a verb.
Getting to the bottom of the problem, never use (or try to use) global variables as they cause silent bugs if there is a better option and you don't understand how it works. In the case that you want a window where you ask the user to provide information on how to select an option that will then be used in the main window then it is advisable to use a QDialog. In the following example, this is done and the "Choose" button is linked to the accept slot so that it closes the window, and that information can be used to know that the "x" button of the window was not closed. Also I have created a property that has the selected text.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.initUI()
self.setGeometry(1050, 500, 400, 50)
self.setWindowTitle("Add")
def initUI(self):
central = QtWidgets.QWidget(self)
self.setCentralWidget(central)
self.deckButton = QtWidgets.QPushButton()
self.deckButton.setText("Choose")
self.deckButton.clicked.connect(self.open_deck_browser)
box = QtWidgets.QVBoxLayout(central)
box.addWidget(self.deckButton)
self.label = QtWidgets.QLabel()
box.addWidget(self.label)
self.label.hide()
def open_deck_browser(self):
dialog = DeckDialog()
if dialog.exec_() == QtWidgets.QDialog.Accepted:
self.label.show()
self.label.setText(dialog.selected_text)
class DeckDialog(QtWidgets.QDialog):
def __init__(self):
super(DeckDialog, self).__init__()
self.initUI()
self.setGeometry(200, 200, 800, 640)
self.setWindowTitle("Choose Deck")
def initUI(self):
self.deckList = QtWidgets.QListWidget()
self.deckList.insertItem(0, "Hello")
self.deckList.insertItem(1, "Hi")
self.deckList.insertItem(2, "Hello There")
self.deckList.item(0).setSelected(True)
self.selectDeck = QtWidgets.QPushButton(self)
self.selectDeck.setText("Choose")
hboxCreateBottomButtons = QtWidgets.QHBoxLayout()
hboxCreateBottomButtons.addStretch()
hboxCreateBottomButtons.addWidget(self.selectDeck)
vboxMain = QtWidgets.QVBoxLayout(self)
vboxMain.addWidget(self.deckList)
vboxMain.addLayout(hboxCreateBottomButtons)
self.selectDeck.clicked.connect(self.accept)
#property
def selected_text(self):
items = self.deckList.selectedItems()
if items:
return items[0].text()
return ""
def main():
app = QtWidgets.QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

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))

Add different text to a window

I have a button and a text label. Each time the button is pressed, i would like text placed from a line edit to be placed onto the window. So far I can only get one text to draw onto the window, even if I create another textlabel. Ive tried seeting a click count determining how many times a user has clicked a button but this doesnt work either. Heres what I have so far, any suggestions?
import sys
import os
from PyQt4 import QtCore, QtGui
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtGui import QApplication
class Window(QMainWindow):
def __init__(self, *args):
QMainWindow.__init__(self, *args)
self.centralWidget = QWidget(self)
self.setCentralWidget(self.centralWidget)
self.setGeometry(450,100,350,680)
self.btn1 = QPushButton("Enter", self.centralWidget)
self.btn1.setGeometry(10,50,150, 20)
self.btn1.clicked.connect(self.enter)
self.edit = QtGui.QLineEdit(self)
self.edit.setGeometry(10, 10, 150, 20)
self.label = QtGui.QLabel(self)
self.label.setGeometry(240, 170,150, 20)
def enter(self):
self.label.setText(self.edit.text())
def main(args):
global app
app = App(args)
app.exec_()
class App(QApplication):
def __init__(self, *args):
QApplication.__init__(self, *args)
self.main = Window()
self.connect(self, SIGNAL("lastWindowClosed()"), self.byebye )
self.main.show()
def byebye( self ):
self.exit(0)
if __name__ == "__main__":
main(sys.argv)
There are a few problems with your example code, the main one being that you are trying to manually arrange the widgets, rather than using a layout.
It's hard to tell from your question exactly what you expect the output to be, so I've assumed you want the text from line-edit to be appended to the label, so that you end up with a series of lines.
Here's a simplified version of your example that hopefully does what you want:
from PyQt4 import QtCore, QtGui
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.btn1 = QtGui.QPushButton("Enter", self)
self.btn1.clicked.connect(self.enter)
self.edit = QtGui.QLineEdit(self)
self.label = QtGui.QLabel(self)
self.label.setAlignment(
QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
layout.addWidget(self.edit)
layout.addWidget(self.btn1)
layout.addWidget(self.label)
self.setCentralWidget(widget)
def enter(self):
text = self.edit.text()
if text:
self.label.setText('%s\n%s' % (self.label.text(), text))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(450, 100, 350, 680)
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