Child dialog doesn't show up in PyQt5 - python

Hi, I'm trying to make simple GUI application using PyQt5, Python 3.4 and Windows 7.
Below code works properly.
# coding: utf-8
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
class MainWnd(QMainWindow):
def __init__(self):
super().__init__()
self.popup_dlg = None
self.init_ui()
def init_ui(self):
self.setGeometry(100, 100, 300, 200)
self.show()
self.popup_dlg = ChildWnd()
class ChildWnd(QDialog):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWnd()
sys.exit(app.exec_())
Two windows are created. One is main window and the other is child window(popup window). But what I want is to make child window's default location is centered of main window.
So I've modified the code like this.
# coding: utf-8
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog
class MainWnd(QMainWindow):
def __init__(self):
super().__init__()
self.popup_dlg = None
self.init_ui()
def init_ui(self):
self.setGeometry(100, 100, 300, 200)
self.show()
self.popup_dlg = ChildWnd(self) # make instance with parent window argument.
class ChildWnd(QDialog):
def __init__(self, parent_wnd):
super().__init__()
self.setParent(parent_wnd) # set child window's parent
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWnd()
sys.exit(app.exec_())
But this code makes problem. Child window doesn't show up. Only main window(=parent window) shows. In Qt's QDialog's manual, I found this.
but if it has a parent, its default location is centered on top of the
parent's top-level widget (if it is not top-level itself).
This is why I added the setParent().
What should I do?
Please help me!!

As specified in the documentation calling setParent will just change the ownership of the QDialog widget. If you want the QDialog widget to be centered within it's parent, you need to pass the parent widget instance to the super constructor of your QDialog:
class ChildWnd(QDialog):
def __init__(self, parent_wnd):
super().__init__(parent_wnd)
self.init_ui()
def init_ui(self):
self.resize(200, 100)
self.show()

Related

Why can't I connect the clicked event of a QPushButton to a function inside a Python class?

I'm trying to connect the clicked event of a QPushButton inside a class (MyButton) to a function inside the same class (print_hello_world) in PyQt5. I'm expecting to print "Hello World" when the user clicks on the button. Can anyone explain why the following code does not work? (i.e. clicking on the button does not print anything)
import sys
from PyQt5.QtWidgets import *
class MyButton:
def __init__(self, parent):
self.parent = parent
self.push_button = QPushButton('Print', parent)
self.push_button.clicked.connect(self.print_hello_world)
def print_hello_world(self):
print("Hello World")
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
button = MyButton(parent=self)
App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec())
The above code will work if I add button.push_button.clicked.connect(lambda:button) after I instantiate the MyButton object in the Window class. Can anyone explain to me why the first code does not work and the following code works?
import sys
from PyQt5.QtWidgets import *
class MyButton:
def __init__(self, parent):
self.parent = parent
self.push_button = QPushButton('Print', parent)
self.push_button.clicked.connect(self.print_hello_world)
def print_hello_world(self):
print("Hello World")
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
button = MyButton(parent=self)
button.push_button.clicked.connect(lambda:button)
App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec())
What is an alternative way to make the first code work by modifying the MyButton class without having to add extra lines of code after instantiating the class?
I am also learning and working on PyQt based software development. I did a few experiments with the code and by calling super initialization, the first code works fine. Here is the modified code:
import sys
from PyQt5.QtWidgets import *
class MyButton(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.push_button = QPushButton('Print', parent)
self.push_button.clicked.connect(self.print_hello_world)
def print_hello_world(self):
print("Hello World")
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
button = MyButton(parent=self)
App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec())
'button' is destroyed because of garbage collection inside 'Window's init method, because it is not an instance variable so 'button' goes out of scope when init method finishes.
So this version works as intended:
import sys
from PyQt5.QtWidgets import *
class MyButton:
def __init__(self, parent):
self.parent = parent
self.push_button = QPushButton('Print', parent)
self.push_button.clicked.connect(self.print_hello_world)
def print_hello_world(self):
print("Hello World")
class Window(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.button = MyButton(parent=self)
App = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(App.exec())

PyQt5 Custom Widget Opens in Another Window

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

how to call resizeEvent function for QDockWidget in QMainWindow

I am trying to implement a QMainWindow with centralWidget and dockWidget. When the user resize the dock widget I want resizeEvent to be called for the dock widget and some values to be returned. I have implemented the resizeEvent for the complete QMainWindow and it is working fine. How can i call resizeEvent for the dockWidget which is a Qwidget without making another class which will inherit from Qwidget and implement the resizeEvent there and afterwards to create an object in QMainwindow. The first given example is working just fine.
class ui(QMainWindow):
def __init__(self):
super().__init__()
self.bottom_dock_widget = DockWidget('Results')
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
def resizeEvent(self, event):
print('mainWindow')
self.update()
class DockWidget(QDockWidget):
def __init__(self, name, image_view):
super().__init__()
self.setWindowTitle(name)
def resizeEvent(self, event):
print('in Dock')
self.update()
Is there a way to be able to implement the example like this:
class ui(QMainWindow):
def __init__(self):
super().__init__()
self.bottom_dock_widget = QDockWidget('Results')
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
but to be able to call resizeEvent only for the dock widget
def resizeEvent(self, event):
print('dock')
self.update()
like on c++ with the scope
def bottom_dock_widget :: resizeEvent(self):
If you want to hear the resize event of a widget it is not necessary to override the resizeEvent() method since it is enough to install an event filter analyzed the QEvent::Resize event
import sys
from PyQt5.QtCore import QEvent, Qt
from PyQt5.QtWidgets import QApplication, QDockWidget, QMainWindow
class UI(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.bottom_dock_widget = QDockWidget("Results")
self.bottom_dock_widget.installEventFilter(self)
self.addDockWidget(Qt.BottomDockWidgetArea, self.bottom_dock_widget)
self.resize(500, 500)
def eventFilter(self, obj, event):
if obj is self.bottom_dock_widget and event.type() == QEvent.Resize:
print("dock")
return super().eventFilter(obj, event)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = UI()
w.show()
sys.exit(app.exec_())

PyQt - Must construct a QApplication before a QWidget

I've realized when creating a class variable to hold a QWidget, it crashes complaining that there is no QApplication. I could break it down to do this minimal example.
import sys
from PyQt4 import QtGui, QtCore
class ThumbContextMenu(QtGui.QMenu):
def __init__(self):
super(ThumbContextMenu, self).__init__()
class Example(QtGui.QWidget):
menu = ThumbContextMenu()
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Quit button')
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
Commenting #menu will launch the application, or putting #menu as an instance variable won't crash too.
Maybe you want to use delayed initialization? Something like this, maybe:
class Example(QtGui.QWidget):
menu = None
def __init__(self):
...
if Example.menu is None:
Example.menu = ThumbContextMenu()
The problem is that menu is class variable, so it is being initialized when Example definition is evaluated, i.e. before you call QApplication constructor.

Replace CentralWidget in MainWindow

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

Categories

Resources