I'm kinda new to PySide.I have a main window object which shows one widget at a time. I've been trying to change the central widget of the QMainWindow class in order to replace the visible Widget in the window when pressing a button. The problem is that the button pressed is in the Widget class, not in the main window class.
say...
class App(QtGui.QMainWindow):
def __init__(self):
super(App, self).__init__()
self.initUI()
def initUI(self):
self.statusBar().showMessage('Listo.') #Status Bar
self.login_screen = LoginScreen()
self.logged_in_screen = LoggedInScreen()
self.setCentralWidget(self.login_screen)
self.setGeometry(300, 300, 450, 600) #Window Size
self.setWindowTitle('PyTransactio - Client') #Window Title
self.setWindowIcon(QtGui.QIcon('icon.png')) #App Icon
self.show()
The pressed button is in the login_screen instance. The method called when the button is clicked is inside the LoginScreen class:
def login(self):
""" Send login data to the server in order to log in """
#Process
self.setParent(None)
Setting the parent widget to None removes the widget (login_screen) from the main window. What should I do in order to get another widget (e.g. logged_in_screen) as the central widget of the main window when the loginButton (inside the login_screen widget) is pressed?
Maybe the login method should be inside the main window class? If so, how can I connect the buttons pressed in login_screen with the main window's method?
You may use a QStackedWidget as central widget and add both the log-in screen and "logged-in" screen to it.
An example usage:
from PyQt4 import QtCore, QtGui
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.central_widget = QtGui.QStackedWidget()
self.setCentralWidget(self.central_widget)
login_widget = LoginWidget(self)
login_widget.button.clicked.connect(self.login)
self.central_widget.addWidget(login_widget)
def login(self):
logged_in_widget = LoggedWidget(self)
self.central_widget.addWidget(logged_in_widget)
self.central_widget.setCurrentWidget(logged_in_widget)
class LoginWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoginWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.button = QtGui.QPushButton('Login')
layout.addWidget(self.button)
self.setLayout(layout)
# you might want to do self.button.click.connect(self.parent().login) here
class LoggedWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(LoggedWidget, self).__init__(parent)
layout = QtGui.QHBoxLayout()
self.label = QtGui.QLabel('logged in!')
layout.addWidget(self.label)
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication([])
window = MainWindow()
window.show()
app.exec_()
If you do not want to use this widget, then I think you'll have to call QMainWindow.setCentralWidget every time you change the central widget.
As to where the login method should be, it depends. Probably you could define a simple interface for your mainwindow to add/remove/show specific central widgets, and call it from the login method of LoginScreen. In this way the LoginScreen class does not have to know about implementation details such as if the central widget is actually a QStackedWidget or this thing is done in an other way.
You can use QMainWindow.setCentralWidget to do this (repeatedly):
#! /usr/bin/env python3
from PySide import QtGui
from PySide import QtCore
import sys
app = QtGui.QApplication(sys.argv)
mw = QtGui.QMainWindow()
w2 = QtGui.QWidget()
pb = QtGui.QPushButton('push me', w2)
l1 = QtGui.QLabel('orig')
l2 = QtGui.QLabel('changed')
mw.setCentralWidget(l1)
pb.clicked.connect(lambda: mw.setCentralWidget(l2))
mw.show()
w2.show()
sys.exit(app.exec_())
Related
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 started out with PyQt5 recently. I wanted to create a custom widget and then insert it into the main window of an application.
The custom Widget:
class ScoreCard(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ScoreCard, self).__init__(parent=parent)
self.setWindowFlags(QtCore.Qt.CustomizeWindowHint)
self.pressing = False
self.init_ui()
self.show()
def init_ui(self):
# Layout in here
And this is the main Application:
from PyQt5.QtWidgets import *
from scorecard import ScoreCard
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.initUi()
self.show()
def initUi(self):
self.setGeometry(300,300,800,700)
window_layout = QVBoxLayout()
recent_playcard = ScoreCard()
window_layout.addWidget(recent_playcard)
self.setLayout(window_layout)
if __name__ == "__main__":
app = QApplication(sys.argv)
execute = MainWindow()
sys.exit(app.exec_())
Why is it that whenever I run the main Application, the custom widget appears in another window? I even tried removing the frame and setting the parent to none, but none of that changed this behavior. How do I fix this?
Looks like I mixed up QMainWindow and QDialog I should be using a central widget for the main application instead of setting a layout..
I'm building a plugin for QGIS using PyQGIS and PyQt5. I want to work with multiple windows. After clicking a button, a new window should open. Ideally, it should overlap window 1. After editing in window 2, it should go back to window 1.
My previous code is like this:
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
loadUi(r"test1.ui", self)
self.button.clicked.connect(self.gotoScreen2)
def gotoScreen2(self):
screen2=Screen2()
widget.addWidget(screen2)
widget.setCurrentIndex(widget.currentIndex()+1)
class Screen2(QDialog):
def __init__(self):
super(Screen2,self).__init__()
loadUi(r"test2.ui", self)
self.pushButton.clicked.connect(self.gotoScreen1)
def gotoScreen1(self):
mainwindow = MainWindow()
widget.addWidget(mainwindow)
widget.setCurrentIndex(widget.currentIndex()+1)
#main
app = QApplication(sys.argv)
widget = QtWidgets.QStackedWidget()
mainwindow = MainWindow()
widget.addWidget(mainwindow)
widget.show()
Building on this, I would like the plugin window to dock on the right in the program. I know that it is possible with the QDockWidget class and addDockWidget (QtCore.Qt.RightDockWidgetArea,...).
But how do I get these class built into my script?
This is my solution (there are now three UIs (windows) that communicate directly with each other.) It works wonderfully in QGIS.
class MainWindow(QDialog):
def __init__(self):
super(MainWindow, self).__init__()
self.gui = loadUi(r"test1.ui", self)
self.button.clicked.connect(self.gotoScreen2)
self.btn_adding.clicked.connect(self.adding)
def gotoScreen2(self):
screen2=Screen2()
widget.setWidget(screen2)
class Screen2(QDialog):
def __init__(self):
super(Screen2,self).__init__()
loadUi(r"test2.ui", self)
self.pushButton.clicked.connect(self.gotoScreen1)
self.pushButton_2.clicked.connect(self.gotoScreen3)
def gotoScreen1(self):
mainwindow = MainWindow()
widget.setWidget(mainwindow)
def gotoScreen3(self):
screen3=Screen3()
widget.setWidget(screen3)
class Screen3(QDialog):
def __init__(self):
super(Screen3,self).__init__()
loadUi(r"test3.ui", self)
self.pushButton.clicked.connect(self.gotoScreen2)
def gotoScreen2(self):
screen2=Screen2()
widget.setWidget(screen2)
widget = QtWidgets.QDockWidget("test")
mainwindow = MainWindow()
widget.setWidget(mainwindow)
#dock on the right side of the screen
iface.addDockWidget(QtCore.Qt.RightDockWidgetArea, widget)
widget.show()
I have a parent and child window. When you push the button from the parent class the child is shown and the parent is hidden. Is there anyway to keep the taskbar icon visible when doing this?
from PyQt5.QtCore import Qt, QDateTime
from PyQt5.QtWidgets import *
from PyQt5 import QtGui
class ParentWindow(QDialog):
def __init__(self):
super(ParentWindow, self).__init__()
self.cw = None
self.button = QPushButton('Go to child')
self.button.clicked.connect(self.child)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
def child(self):
self.cw = ChildWindow(self)
self.hide()
def parent(self):
self.cw.close()
self.show()
class ChildWindow(QDialog):
def __init__(self, parent):
super(ChildWindow, self).__init__(parent)
self.button = QPushButton('Go to parent')
self.button.clicked.connect(self.parent().parent)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = ParentWindow()
sys.exit(app.exec_())
The problem is that a child QDialog "shares" the same taskbar entry as its parent, and when the parent is hidden, so does the taskbar entry, no matter if a child is visible.
A possible solution is to not hide the parent, but minimize it. Keep in mind that this has only been tested as working on Windows, on Linux you can have unexpected behavior (depending on how the window manager relates to modal windows) and I don't know how it will work out on MacOs.
The idea is to use showMinimized() on the parent, then show the child, activate it to make it visible on top and, most importantly, use exec_() to ensure it has full control and make it behave as a dialog should.
Then we connect both the accepted and rejected signals of the dialog to restore the main window, and connect the clicked signal to accept.
class ParentWindow(QDialog):
def __init__(self):
super(ParentWindow, self).__init__()
self.cw = None
self.button = QPushButton('Go to child')
self.button.clicked.connect(self.child)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.show()
def child(self):
self.cw = ChildWindow(self)
self.showMinimized()
self.cw.show()
self.cw.activateWindow()
self.cw.exec_()
class ChildWindow(QDialog):
def __init__(self, parent):
super(ChildWindow, self).__init__(parent)
self.button = QPushButton('Go to parent')
self.button.clicked.connect(self.accept)
layout = QHBoxLayout()
layout.addWidget(self.button)
self.setLayout(layout)
self.accepted.connect(self.parent().showNormal)
self.rejected.connect(self.parent().showNormal)
PS: I ended up not using a custom function to call back the parent at all, but remember that you shouldn't overwrite function/property names like parent.
I would like to use a QMenu as a permanent widget in the gui. (I like its appearance and layout, and the fact that as soon as I hover over it, the requisite menu pops up, no clicking needed. It would be a pain in the neck to try and emulate it with a custom widget.) I have tried adding it to a parent widget's layout, but after the first time it is used, it disappears. How would I go about keeping it there?
I can't find any option in QMenu that would disable auto-hide, so simplest way would be a subclass that overrides hideEvent. hideEvent is fired just before hide() completes. That means you can't intercept/ignore hide() but you can re-show it:
class PermanentMenu(QtGui.QMenu):
def hideEvent(self, event):
self.show()
Just make your top-level menu from PermanentMenu and it should be fine.
A simple example using it:
import sys
from PyQt4 import QtGui
class PermanentMenu(QtGui.QMenu):
def hideEvent(self, event):
self.show()
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.menu = PermanentMenu()
self.menu.addAction('one')
self.menu.addAction('two')
self.submenu = self.menu.addMenu('submenu')
self.submenu.addAction('sub one')
self.submenu.addAction('sub two')
self.submenu2 = self.menu.addMenu('submenu 2')
self.submenu2.addAction('sub 2 one')
self.submenu2.addAction('sub 2 two')
layout = QtGui.QHBoxLayout()
layout.addWidget(self.menu)
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())