PyQt Make parent GUI wait till child GUI is closed - python

I am new to pyqt.I am trying to invoke a child GUI when a button is clicked in the parent GUI. In this process, parent GUI has to wait for the child GUI to be closed by the user after selecting some inputs. But this is not happening, Parent GUI does execute the next lines after which the child GUI has been invoked. Below is the code where I am passing an argument to child GUI from parent GUI. The child GUI will return value based on OK/Cancel button click
Code:
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
self.swwidget = QtGui.QWidget()
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.swwidget.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.swwidget.setWindowTitle('Switches')
self.swwidget.setLayout(self.swlayout)
self.swwidget.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.swwidget.close()
return self.addsw
def swreject(self):
self.swwidget.close()
return None
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
print ("Child GUI closed")
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
After the button "Test" is clicked, a child GUI will pop-up. I don't want the statement "Child GUI Closed" to be printed till the child GUI is closed.
Can someone suggest me how to achieve this functionality ?

You have to handle closeEvent to perform operations when a window wants to close, also since your Child class inherits from QWidget which means it's a QWidget itself you dont need to create another one with self.swwidget
import sys
from PyQt4 import QtGui,QtCore,Qt
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Child(QtGui.QWidget):
def __init__(self,switches=None):
super(Child,self).__init__()
# self.swwidget = QtGui.QWidget() # you don't need to do this you can add all the properties to self
self.swlayout = QtGui.QGridLayout()
switches = ['abc1','def1']
switches.sort()
self.switches = switches
def switchesUI(self):
self.setWindowModality(QtCore.Qt.ApplicationModal)
self.swl = len(self.switches)
self.sw = {}
self.addsw = []
print ("I am in switchesUI")
#Add the switches to layout dynamically
for i in range(self.swl):
self.sw[i] = QtGui.QCheckBox(self.switches[i])
self.swlayout.addWidget(self.sw[i],i,0)
self.swbuttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel);
self.swbuttonbox.setOrientation(QtCore.Qt.Horizontal)
self.swlayout.addWidget(self.swbuttonbox)
self.setWindowTitle('Switches')
self.setLayout(self.swlayout)
self.show()
self.connect(self.swbuttonbox,QtCore.SIGNAL("accepted()"),self.swaccept)
self.connect(self.swbuttonbox,QtCore.SIGNAL("rejected()"),self.swreject)
def swaccept(self):
for i in range(self.swl):
if self.sw[i].isChecked():
self.addsw.append(self.switches[i])
self.close()
return self.addsw
def swreject(self):
self.close()
return None
def closeEvent(self, event):
print ("Child GUI closed")
class Parent(QtGui.QWidget):
def __init__(self):
super(Parent,self).__init__()
QtGui.QWidget.__init__(self)
self.button = QtGui.QPushButton('Test', self)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.button)
self.assw = ['Test1','Test2']
self.CH = Child(self.assw)
self.connect(self.button,SIGNAL("clicked()"),self.popup)
def popup(self):
self.CH.switchesUI()
def main():
app = QtGui.QApplication(sys.argv)
form = Parent()
form.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Related

How to open and close my SubWindow In QMdiArea in PyQt5?

In QMidArea how to open a SubWindow? My Programs as follows. My intention to open/attach my second program in SubWindow. But Nothing Will happen. visible Only blank Window. How to resolve it?
How to attach my file in QMdi Sub-window ? and after my work, how to close the sub-window properly?
Main Programme
import sys,os
from PyQt5.QtWidgets import *
from sample_countrypage import Countrypage
class MainPage(QMainWindow):
def __init__(self):
super().__init__()
self.mdi = QMdiArea()
self.setWindowTitle(" Sample Programme")
self.setGeometry(100,100,1600,600)
self.Ui()
self.show()
def Ui(self):
self.btn1=QPushButton("Country")
self.btn1.setFixedSize(100, 30)
self.btn1.clicked.connect(self.countrypage)
self.left_layout = QVBoxLayout()
self.main_layout = QHBoxLayout()
self.left_layout.setContentsMargins(3,5,5,3)
self.left_layout.addWidget(self.btn1)
self.left_layout.addStretch()
self.main_layout.setSpacing(5)
self.main_layout.setContentsMargins(0,0,0,0)
self.main_layout.addLayout(self.left_layout)
self.main_layout.addStretch()
self.setLayout(self.main_layout)
widget = QWidget()
widget.setLayout(self.main_layout)
self.setCentralWidget(widget)
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
# subwindow.setFixedSize(500,500)
subwindow.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
mainwindow = MainPage()
app.setStyle("fusion")
mainwindow.show()
sys.exit(app.exec_())
Second Program
import sys,os
from PyQt5.QtWidgets import *
class Countrypage(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("Country Page")
self.btn1 = QPushButton("Accept")
self.btn1.clicked.connect(self.result)
self.btn2 = QPushButton("Re Enter")
self.form_layout = QFormLayout()
self.form_layout.addRow("Country",QLineEdit())
self.form_layout.addRow("continent",QLineEdit())
self.layout_btn = QHBoxLayout()
self.layout_btn.addStretch()
self.layout_btn.addWidget(self.btn1)
self.layout_btn.addWidget(self.btn2)
self.layout_country = QVBoxLayout()
self.layout_country.addLayout(self.form_layout)
self.layout_country.addLayout(self.layout_btn)
self.layout_country.addStretch()
self.setLayout(self.layout_country)
def result(self):
print("bye")
exec .close()
if __name__=="__main__":
app = QApplication(sys.argv)
countrywin = Countrypage()
countrywin.show()
sys.exit(app.exec_())
First of all, there are two main issues with your code:
You never added the mdi area to the main layout (and you also tried to set the layout for the QMainWindow, which is forbidden);
exec is a python builtin, and has no close attribute; if you want to close the widget, you have to call self.close();
Then, the setWidget() method of QMdiSubWindow reparents the widget:
QMdiSubWindow takes temporary ownership of widget;
This means that if you want to close the sub window that contains the widget from that widget, you have to check the parent and eventually close it, as soon as you verify that it's an instance of QMdiSubWindow.
class Countrypage(QWidget):
# ...
def result(self):
print("bye")
# ensure that the parent is actually a subwindow
if isinstance(self.parent(), QMdiSubWindow):
self.parent().close()
else:
self.close()
Alternatively, you can use a custom signal and connect that when creating the subwindow.
class Countrypage(QWidget):
closeRequested = pyqtSignal()
# ...
def result(self):
print("bye")
self.closeRequested.emit()
class MainPage(QMainWindow):
# ...
def countrypage(self):
print("country page")
self.countrywindow = Countrypage()
subwindow = QMdiSubWindow()
subwindow.setWidget(self.countrywindow)
self.mdi.addSubWindow(subwindow)
subwindow.show()
self.countrywindow.closerequested.connect(subwindow.close)
If you want to close the active subwindow from the mdi area (or outside of it) and no matter what that sub window is, just call self.mdi.closeActiveSubWindow().
Note that if you're going to create multiple Countrypage instances, there's no point in creating an instance attribute (self.countrywindow) as it will always be overwritten as soon as another instance will be created. Adding the widget to the subwindow and that subwindow to the mdi area will automatically create a persistent reference (due to the parenting); if you need a python reference to existing pages, then create a list as an instance member in the __init__ (eg. self.pages = []) and add the new instances to that list.

How to get a PyQt5 QPushButton to do different commands on different button clicks

I wish to have the QPushButton do different things on different clicks. One the first click it should execute one command and on the next click, it should execute the other command. I've tried to make a program to do it but it only executes one command, not the other
my code I:
import PyQt5.QtWidgets as pyqt
import sys
ongoing = False
class Stuff(pyqt.QWidget):
def __init__(self):
super().__init__()
self.windows()
def windows(self):
w = pyqt.QWidget()
layout = pyqt.QGridLayout()
self.setLayout(layout)
button = pyqt.QPushButton('click me', w)
layout.addWidget(button)
if not ongoing:
button.clicked.connect(click_one)
else:
button.clicked.connect(click_two)
self.show()
w.show()
def click_one():
global ongoing
print('one')
ongoing = not ongoing
def click_two():
global ongoing
print('two')
ongoing = not ongoing
if __name__ == '__main__':
app = pyqt.QApplication(sys.argv)
x = Stuff()
app.exec_()
What should I do to fix this?
Since the value of ongoing is False when the class is initialized, the button's clicked signal gets connected to click_one(). Connect the button to an initial slot and then call the desired function based on the value of ongoing.
class Stuff(pyqt.QWidget):
def __init__(self):
super().__init__()
self.windows()
def windows(self):
w = pyqt.QWidget()
layout = pyqt.QGridLayout()
self.setLayout(layout)
button = pyqt.QPushButton('click me', w)
layout.addWidget(button)
button.clicked.connect(on_click)
self.show()
w.show()
def on_click():
global ongoing
if not ongoing:
click_one()
else:
click_two()
I suggest rewriting the code with the functions and ongoing variable belonging to the class. The QWidget assigned to variable w seems redundant because the QPushButton is then added to the layout of the class, so its parent gets changed anyways.
class Stuff(pyqt.QWidget):
def __init__(self):
super().__init__()
self.ongoing = False
self.windows()
def windows(self):
layout = pyqt.QGridLayout(self)
button = pyqt.QPushButton('click me')
layout.addWidget(button)
button.clicked.connect(self.on_click)
self.show()
def on_click(self):
self.click_one() if not self.ongoing else self.click_two()
self.ongoing = not self.ongoing
def click_one(self):
print('one')
def click_two(self):
print('two')
Also you might be interested in using a checkable button.

How to get result from corfirmation dialog

I have problem with the results of my pop-up window. Below I have shown part of my code to understand the problem.
It's a kind of pop-up window where the user makes some choice in the GUI. After this it should show a window where there will be the question "Are you sure?", and two buttons "Yes" and "No".
The problem is that when I test the code below (before and after the msg.show()), I have the same value set as False.
Why doesnt it work like this:
Before function -> False
Show my window and wait to click the button
If I clicked button "Yes", then give True, else False
How I can handle this properly? Is there another approach?
from PyQt4 import QtCore, QtGui
from Message import Ui_Message
import sys
class MessageBox(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent=None)
self.msg = Ui_Message()
self.msg.setupUi(self)
self.confirmed=False
self.declined=False
QtCore.QObject.connect(self.msg.NoButton, QtCore.SIGNAL(("clicked()")), self.Declined)
QtCore.QObject.connect(self.msg.YesButton, QtCore.SIGNAL(("clicked()")), self.Confirmed)
def Confirmed(self):
self.confirmed = True
MessageBox.close(self)
return True
def Declined(self):
self.declined = True
MessageBox.close(self)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
msg = MessageBox()
print('Befor show window',msg.confirmed)
msg.show()
print('After show window', msg.confirmed)
sys.exit(app.exec_())
Your example doesn't work, because you are printing "After show window" before the window has closed. It is the exec() method that blocks, not the show() method, so your example would need to be written like this:
app = QtGui.QApplication(sys.argv)
msg = MessageBox()
print('Before show window', msg.confirmed)
msg.show()
app.exec_() # this blocks, waiting for close
print('After show window', msg.confirmed)
sys.exit()
However, a much more realistic example showing how to use a dialog to confirm an action would be something like this:
import sys
from PyQt4 import QtCore, QtGui
class MessageBox(QtGui.QDialog):
def __init__(self, parent=None):
super(MessageBox, self).__init__(parent)
self.yesButton = QtGui.QPushButton('Yes')
self.noButton = QtGui.QPushButton('No')
layout = QtGui.QGridLayout(self)
layout.addWidget(QtGui.QLabel('Are you sure?'), 0, 0)
layout.addWidget(self.yesButton, 1, 0)
layout.addWidget(self.noButton, 1, 1)
self.yesButton.clicked.connect(self.accept)
self.noButton.clicked.connect(self.reject)
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Do Something')
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
if self.confirmSomething():
print('Yes')
else:
print('No')
def confirmSomething(self):
msg = MessageBox(self)
result = msg.exec_() == QtGui.QDialog.Accepted
msg.deleteLater()
return result
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
app.exec_()

Using the QWizard next slot in pyside

I've created a wizard with Pyside.
On one page, I create a new thread, which starts an installer.
When the installer is ready, the Wizard should go forward automatically, without clicking the next button.
I've read the pyside documentation, and now my understanding is, that QWizard has a next function. But how can I use this function?
My test is working fine:
from PySide.QtGui import *
from PySide.QtCore import *
...
...
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
def initializePage(self):
self.setTitle("Install")
label = QLabel("Install")
label.setWordWrap(True)
layout = QVBoxLayout()
self.progressBar = QProgressBar(self)
self.progressBar.setRange(0,1)
self.progressBar.setRange(0,0)
layout.addWidget(self.progressBar)
layout.addWidget(label)
self.setLayout(layout)
self.myTask = TaskThread()
self.myTask.start()
self.myTask.taskFinished.connect(self.Finished)
def Finished(self):
print("finish")
def isComplete(self):
return False
class TaskThread(QThread):
taskFinished = Signal()
def run(self):
a = 0
while a != 10000:
print("test")
a += 1
self.taskFinished.emit()
And when I try to use the next function I try:
self.CallNext = QWizard().next
self.myTask.taskFinished.connect(self.CallNext)
And also:
self.myTask.taskFinished.connect(QWizard().next)
But this is not working
This connection should be done in the context where the QWizard and QWizardPage exist, but before that we must move the creation of the QThread to the constructor, for example in the following example I do in the main:
class Install(QWizardPage):
def __init__(self, parent=None):
super(Install, self).__init__(parent)
self.myTask = TaskThread()
def initializePage(self):
[...]
self.setLayout(layout)
self.myTask.start()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
wizard = QWizard()
install = Install()
install.setTitle("installer")
install.myTask.taskFinished.connect(wizard.next)
wizard.addPage(install)
page = QWizardPage()
page.setTitle("next Page")
wizard.addPage(page)
wizard.show()
sys.exit(wizard.exec_())

Printing button toolbar PySide Python

I have a browser application written in python with PySide Qt. But now I want to add a button in the toolbar to print the website. How do I do this? Because CTRL + P is not working in the application.
Here is the code:
import sys
from PySide import QtCore, QtGui, QtWebKit, QtHelp, QtNetwork
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
Action1 = QtGui.QAction('Google', self)
Action1.setShortcut('Ctrl+M')
Action1.triggered.connect(self.load_message)
self.toolbar1 = self.addToolBar('Google')
self.toolbar1.addAction(Action1)
Action2 = QtGui.QAction('Yahoo', self)
Action2.setShortcut('Ctrl+H')
Action2.triggered.connect(self.load_list)
self.toolbar2 = self.addToolBar('Yahoo')
self.toolbar2.addAction(Action2)
exitAction = QtGui.QAction('Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(self.on_exit)
self.toolbar3 = self.addToolBar('Exit')
self.toolbar3.addAction(exitAction)
self.resize(750, 750)
self.setWindowTitle('Browser')
self.web = QtWebKit.QWebView(self)
self.web.load(QtCore.QUrl('http://www.google.com'))
self.setCentralWidget(self.web)
def on_exit(self):
QtGui.qApp.quit
def load_message(self):
self.web.load(QtCore.QUrl('http://www.google.com'))
def load_list(self):
self.web.load(QtCore.QUrl('http://www.yahoo.com'))
app = QtGui.QApplication(sys.argv)
app.setWindowIcon(QtGui.QIcon('myicon.ico'))
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
In your __init__ method add a Print action:
printAction = QtGui.QAction('Print', self)
printAction.setShortcut('Ctrl+P')
printAction.triggered.connect(self.do_print)
self.toolbar4 = self.addToolBar('Print')
self.toolbar4.addAction(printAction)
And create a do_print method:
def do_print(self):
p = QtGui.QPrinter()
p.setPaperSize(QtGui.QPrinter.A4)
p.setFullPage(True)
p.setResolution(300)
p.setOrientation(QtGui.QPrinter.Portrait)
p.setOutputFileName('D:\\test.pdf')
self.web.print_(p)
This will print to a file D:\test.pdf.
To configure your printer differently see the QPrinter documentation. Also if you want a print preview dialog see the QPrintPreviewDialog docs.
If you want a standard print dialog the use:
def do_print(self):
p = QtGui.QPrinter()
dialog = QtGui.QPrintDialog(p)
dialog.exec_()
self.web.print_(p)

Categories