Block QMainWindow while child widget is alive , pyqt - python

I want when the user press the button a form will appear after MainWindow is blocked pending form filling

You don't need to do anything that the other answers suggest. Using any exec() methods is a surefire way to have bugs, since suddenly your gui code can be reentered. Don't do it.
All you need to do is to set proper window modality before you show it (that's the important part). So:
widget.setWindowModality(Qt.ApplicationModal)
widget.show()
If you wish the window to block only some other window, not the entire application:
widget.setWindowFlags(widget.windowFlags() | Qt.Window)
widget.setParent(otherWindow)
widget.setWindowModality(Qt.WindowModal)
widget.show()
Note that this code is for PyQt4 only, it won't work with Qt 5 as there, the window functionality belongs in a class separate from QWidget.

You need to use a QDialog and show it using exec, which will block the rest of the application until it is closed. The return value of exec also tells you whether the form was closed without committing changes (i.e. cancelled).
Here is a simple demo script that shows how to use a QDialog:
from PyQt4 import QtCore, QtGui
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
QtGui.QDialog.__init__(self, parent)
self.checkbox1 = QtGui.QCheckBox('Option one', self)
self.checkbox2 = QtGui.QCheckBox('Option two', self)
self.buttonOk = QtGui.QPushButton('Ok', self)
self.buttonOk.clicked.connect(self.accept)
self.buttonCancel = QtGui.QPushButton('Cancel', self)
self.buttonCancel.clicked.connect(self.reject)
layout = QtGui.QGridLayout(self)
layout.addWidget(self.checkbox1, 0, 0, 1, 2)
layout.addWidget(self.checkbox2, 1, 0, 1, 2)
layout.addWidget(self.buttonOk, 2, 0)
layout.addWidget(self.buttonCancel, 2, 1)
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
widget = QtGui.QWidget(self)
layout = QtGui.QVBoxLayout(widget)
self.button = QtGui.QPushButton('Show Dialog', self)
self.button.clicked.connect(self.handleButton)
layout.addWidget(self.button)
self.setCentralWidget(widget)
def handleButton(self):
dialog = Dialog(self)
if dialog.exec_() == QtGui.QDialog.Accepted:
print('Option one: %s' % dialog.checkbox1.isChecked())
print('Option two: %s' % dialog.checkbox2.isChecked())
else:
print('Cancelled')
dialog.deleteLater()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 200, 100)
window.show()
sys.exit(app.exec_())

This is what you need
self.setWindowModality(QtCore.Qt.ApplicationModal)

Ok , so you want to block the parent window until the child window has been closed.
dialog = QInputDialog()
dialog.exec_()
Use the exec_() function , it will block until the child window is not closed
for further info :
launch a PyQT window from a main PyQt window, and get the user input?
Python - make a window appear on top of another, block access to other windows until button clicked

Subclass your QDialog or your QWidget with your form, and then connect it like this in the constructory of your main window. You will want to convert this code from c++ to python:
QObject::connect(myPushButton, SIGNAL(clicked), this, SLOT(on_myPushButton()));
//...
void MainWindow::on_myPushButton()
{
Dialog d;
int retVal = d.exec();// this is a blocking call
// Here the user has finished filling out the form.
// save any data that should be in the form, or respond to the retVal
}
EDIT: Added link to docs on using QDialog::exec()
http://qt-project.org/doc/qt-5/qdialog.html#exec
Hope that helps.

must create Widget inherits from Qdialog
AjoutArBase, AjoutArForm = uic.loadUiType('ajoutArticle.ui')
class AjoutArticle(AjoutArBase,QtGui.QDialog):

Related

How to create an independent non-modal dialog

I'm trying to extend this solution Non modal dialog
from PyQt5 import QtWidgets
dialog = None
class Dialog(QtWidgets.QDialog):
def __init__(self, *args, **kwargs):
super(Dialog, self).__init__(*args, **kwargs)
self.setWindowTitle('A floating dialog')
self.resize(250,250)
class Window(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
button = QtWidgets.QPushButton('Open Dialog', self)
button.clicked.connect(self.handleOpenDialog)
self.resize(300, 200)
self._dialog = None
global dialog
dialog = Dialog(self)
dialog.show()
def handleOpenDialog(self):
if self._dialog is None:
self._dialog = QtWidgets.QDialog(self)
self._dialog.resize(200, 100)
self._dialog.exec_()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
Apology if title wasn't relevant. I want to have a dialog/window that is independent of all existing window/dialogs, and can be always interacted with, i.e. GUI loop of application window/any dialogs doesn't block this non-model dialog. For simplicity, I have used global variable dialog in above code snippet which will hold the non-modal dialog instance.
When above program is run, the main window appears along-with the non-modal dialog, and both dialogs are user interactive, but when the button is clicked, the GUI loop of self._dialog starts, and user can no longer interact with the floating dialog, and application window. What I want is to be able to interact with dialog but not with Window
I want behavior similar to the example below:
I opened help dialog from main window, then I opened a non-modal dialog which appears on top of the main window, and can not interact with the main window, but still doesn't block help dialog/window and allows user to interact with this non-modal window i.e. the help dialog in the example.
When a dialog is opened with exec(), it will default to being application-modal. This means it will block all other windows in the application, regardless of whether they're parented to other windows or not. To make a dialog modal for only one window, it must be parented to that window and also have its modality explicitly set to window-modal.
For a dialog to be fully non-modal with respect to all other windows (and any of their modal dialogs), it must have no parent and then be opened with show(). However, a side-effect of this is that it won't be automatically closed when the main-window is closed. To work around this, it can be explicitly closed in the closeEvent() of the main-window.
Here is a simple demo that implements all of the above:
import sys
from PyQt5 import QtCore, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('Main Window')
self.setGeometry(400, 100, 300, 200)
self._help_dialog = None
self._other_dialog = None
self.buttonHelp = QtWidgets.QPushButton('Open Help')
self.buttonHelp.clicked.connect(self.handleOpenHelp)
self.buttonDialog = QtWidgets.QPushButton('Open Dialog')
self.buttonDialog.clicked.connect(self.handleOpenDialog)
layout = QtWidgets.QHBoxLayout(self)
layout.addWidget(self.buttonDialog)
layout.addWidget(self.buttonHelp)
self.handleOpenHelp()
def handleOpenDialog(self):
if self._other_dialog is None:
self._other_dialog = QtWidgets.QDialog(self)
self._other_dialog.setWindowModality(QtCore.Qt.WindowModal)
self._other_dialog.setWindowTitle('Other Dialog')
self._other_dialog.resize(200, 100)
self._other_dialog.exec_()
def handleOpenHelp(self):
if self._help_dialog is None:
self._help_dialog = QtWidgets.QDialog()
self._help_dialog.setWindowTitle('Help Dialog')
self._help_dialog.setGeometry(750, 100, 250, 250)
self._help_dialog.show()
def closeEvent(self, event):
if self._help_dialog is not None:
self._help_dialog.close()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Generating a QCloseEvent won't close QMainWindow

I'm trying to do something quite simple: add a menu bar with an Exit action that will close a QMainWindow when it is selected. However, when I actually click Exit, it doesn't close the application. A SSCCE:
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
widget = QtGui.QWidget()
self.setCentralWidget(widget)
self.menu_bar = QtGui.QMenuBar(self)
menu = self.menu_bar.addMenu('File')
exit_action = QtGui.QAction('Exit', self)
exit_action.triggered.connect(lambda:
self.closeEvent(QtGui.QCloseEvent()))
menu.addAction(exit_action)
self.setMenuBar(self.menu_bar)
def closeEvent(self, event):
print('Calling')
print('event: {0}'.format(event))
event.accept()
app = QtGui.QApplication(sys.argv)
form = Window()
form.show()
sys.exit(app.exec_())
What is really confusing me is that when I click Exit from the File menu, I get the following output:
Calling
event: <PyQt4.QtGui.QCloseEvent object at 0x024B7348>
and the application does not exit.
If I click the top-right corner X, I get the same thing (down to the same memory address for the event object):
Calling
event: <PyQt4.QtGui.QCloseEvent object at 0x024B7348>
and the application does exit.
This is on Windows 7 64-bit, Python 2.7.2, PyQt 4.8.6.
Document says,
The QCloseEvent class contains parameters that describe a close event.
Close events are sent to widgets that the user wants to close, usually
by choosing "Close" from the window menu, or by clicking the X title
bar button. They are also sent when you call QWidget.close() to close
a widget programmatically.
Your can call directly with signal close not by QCloseEvent, please call self.close().
from PyQt4 import QtGui, QtCore
import sys
class Window(QtGui.QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
widget = QtGui.QWidget()
self.setCentralWidget(widget)
self.menu_bar = QtGui.QMenuBar(self)
menu = self.menu_bar.addMenu('File')
exit_action = QtGui.QAction('Exit', self)
exit_action.triggered.connect(self.close)
menu.addAction(exit_action)
self.setMenuBar(self.menu_bar)
def closeEvent(self, event):
print('Calling')
print('event: {0}'.format(event))
event.accept()
app = QtGui.QApplication(sys.argv)
form = Window()
form.show()
sys.exit(app.exec_())
The close event doesn't actually make the window close, it's just triggered when the window is already closing. To actually make the window close, you need to call self.close(), which will have the side effect of triggering a QCloseEvent. So simply use this:
exit_action.triggered.connect(self.close)
The documentation of close describes the interaction between close and closeEvent:
bool QWidget.close (self)
This method is also a Qt slot with the C++ signature bool close().
Closes this widget. Returns true if the widget was closed; otherwise
returns false.
First it sends the widget a QCloseEvent. The widget is hidden if it
accepts the close event. If it ignores the event, nothing happens. The
default implementation of QWidget.closeEvent() accepts the close
event.

How to pop out a separate window from a tabWidget in PySide Qt

I have an extended main window with a QtGui.QTabWidget added to it. I am creating several widgets extended from QtGui.QWidget which I can add and remove to the tab widget.
What I would like to do is have a "pop-out" button that causes the child widget to be removed from the tab widget and come up as it's own independent window (and a "pop-in" button to put it back into the main window). The same sort of idea as Gtalk-in-Gmail has.
Note that if I close the main window, the other "tabs" or "windows" should also close, and I should be able to put all the windows side-by-side and have them all visible and updating at the same time. (I will be displaying near-realtime data).
I am new to Qt, but if I'm not mistaken, if a Widget has no parent it comes up independently. This works, but I then have no idea how I could "pop" the window back in.
class TCWindow(QtGui.QMainWindow):
.
.
.
def popOutWidget(self, child):
i = self.tabHolder.indexOf(child)
if not i == -1:
self.tabCloseRequested(i)
self.widgets[i].setParent(None)
self.widgets[i].show()
My gut says that there should still be a parent/child relationship between the two.
Is there a way to keep the parent but still have the window come up independently, or am I misunderstanding Qt's style?
Otherwise, would creating a variable in the child to hold a link to the main window (like self.parentalUnit = self.parent()) be a good idea or a hackish/kludgy idea?
Leave the parent as is. If you remove the parent, then closing main window won't close 'floating' tabs, since they are now top-level windows. windowFlags defines if a widget is window or a child widget. Basically, you need to alternate between QtCore.Qt.Window and QtCore.Qt.Widget
Below is a small but complete example:
#!/usr/bin/env python
# -.- coding: utf-8 -.-
import sys
from PySide import QtGui, QtCore
class Tab(QtGui.QWidget):
popOut = QtCore.Signal(QtGui.QWidget)
popIn = QtCore.Signal(QtGui.QWidget)
def __init__(self, parent=None):
super(Tab, self).__init__(parent)
popOutButton = QtGui.QPushButton('Pop Out')
popOutButton.clicked.connect(lambda: self.popOut.emit(self))
popInButton = QtGui.QPushButton('Pop In')
popInButton.clicked.connect(lambda: self.popIn.emit(self))
layout = QtGui.QHBoxLayout(self)
layout.addWidget(popOutButton)
layout.addWidget(popInButton)
class Window(QtGui.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__()
self.button = QtGui.QPushButton('Add Tab')
self.button.clicked.connect(self.createTab)
self._count = 0
self.tab = QtGui.QTabWidget()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.tab)
def createTab(self):
tab = Tab()
tab.setWindowTitle('%d' % self._count)
tab.popIn.connect(self.addTab)
tab.popOut.connect(self.removeTab)
self.tab.addTab(tab, '%d' % self._count)
self._count += 1
def addTab(self, widget):
if self.tab.indexOf(widget) == -1:
widget.setWindowFlags(QtCore.Qt.Widget)
self.tab.addTab(widget, widget.windowTitle())
def removeTab(self, widget):
index = self.tab.indexOf(widget)
if index != -1:
self.tab.removeTab(index)
widget.setWindowFlags(QtCore.Qt.Window)
widget.show()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = Window()
w.show()
sys.exit(app.exec_())
In Qt, layout takes ownership over the widgets that are added to layout, so let it handle parentship.
You can create another widget (with no parent) which will be hidden until you press pop-out button and when it is pressed, you remove "pop-out widget" from its original layout and add it to layout of the hidden widget. And when pop-in button pressed - return widget to it's original layout.
To close this hidden window, when closing main window, you will need to redefine closeEvent(QCloseEvent* ev) to something like this (sorry for c++, but i bet, in python it's all the same):
void MainWindow::closeEvent(QCloseEvent* ev)
{
dw->setVisible(false); // independent of mainwindow widget
sw->setVisible(false); // independent of mainwindow widget
QWidget::closeEvent(ev); //invoking close event after all the other windows are hidden
}

Qt/PyQt: How do I use a QMenu as a permanent widget?

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

Popup, non-modal, in-line Dialog in PyQT

It's difficult to describe what I want to do here, so here's what I want:
-Click a button that "pops up" a dialog.
-This dialog, however, should be non-modal.
-I'd also like for it to be sort of 'locked' with the parent app so if I moved it, it too would come with.
It seems like i'm more or less trying to describe an in-line popup, but I'm not sure if that's what it's called, or even how I should go about approaching this problem. So, PyQt gurus, how would you make something like this? Thanks in advance.
To get a modeless dialog, open it with show() rather than exec_().
To ensure that the dialog is "locked with the parent app", set a parent window in the dialog's constructor.
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
button = QtGui.QPushButton('Open Dialog', self)
button.clicked.connect(self.handleOpenDialog)
self.resize(300, 200)
self._dialog = None
def handleOpenDialog(self):
if self._dialog is None:
self._dialog = QtGui.QDialog(self)
self._dialog.resize(200, 100)
self._dialog.show()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
for Qt5:
I ignore why dialog.setWindowModality(QtCore.Qt.NonModal) won't work, but
dialog.run()
dialog.exec_()
will do. run will make the dialog non modal and exec_ will block it until the user input.

Categories

Resources