QDialog - Prevent Closing in Python and PyQt - python

I have a login screen dialog written using pyqt and python and it shows a dialog pup up when it runs and you can type in a certin username and password to unlock it basicly. It's just something simple I made in learning pyqt. I'm trying to take and use it somewhere else but need to know if there is a way to prevent someone from using the x button and closing it i would like to also have it stay on top of all windows so it cant be moved out of the way? Is this possible? I did some research and couldn't find anything that could help me.
Edit:
as requested here is the code:
from PyQt4 import QtGui
class Test(QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
self.textUsername = QtGui.QLineEdit(self)
self.textPassword = QtGui.QLineEdit(self)
self.loginbuton = QtGui.QPushButton('Test Login', self)
self.loginbuton.clicked.connect(self.Login)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.textUsername)
layout.addWidget(self.textPassword)
layout.addWidget(self.loginbuton)
def Login(self):
if (self.textUsername.text() == 'Test' and
self.textPassword.text() == 'Password'):
self.accept()
else:
QtGui.QMessageBox.warning(
self, 'Wrong', 'Incorrect user or password')
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
if Test().exec_() == QtGui.QDialog.Accepted:
window = Window()
window.show()
sys.exit(app.exec_())

Bad news first, it is not possible to remove the close button from the window, based on the Riverbank mailing system
You can't remove/disable close button because its handled by the
window manager, Qt can't do anything there.
Good news, you can override and ignore, so that when the user sends the event, you can ignore or put a message or something.
Read this article for ignoring the QCloseEvent
Also, take a look at this question, How do I catch a pyqt closeEvent and minimize the dialog instead of exiting?
Which uses this:
class MyDialog(QtGui.QDialog):
# ...
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
# when you want to destroy the dialog set this to True
self._want_to_close = False
def closeEvent(self, evnt):
if self._want_to_close:
super(MyDialog, self).closeEvent(evnt)
else:
evnt.ignore()
self.setWindowState(QtCore.Qt.WindowMinimized)

You can disable the window buttons in PyQt5.
The key is to combine it with "CustomizeWindowHint",
and exclude the ones you want to be disabled.
Example:
#exclude "QtCore.Qt.WindowCloseButtonHint" or any other window button
self.setWindowFlags(
QtCore.Qt.Window |
QtCore.Qt.CustomizeWindowHint |
QtCore.Qt.WindowTitleHint |
QtCore.Qt.WindowMinimizeButtonHint
)
Result with QDialog:
Reference: https://doc.qt.io/qt-5/qt.html#WindowType-enum
Tip: if you want to change flags of the current window, use window.show()
after window.setWindowFlags,
because it needs to refresh it, so it calls window.hide().
Tested with QtWidgets.QDialog on:
Windows 10 x32,
Python 3.7.9,
PyQt5 5.15.1
.

I don't know if you want to do this but you can also make your window frameless. To make window frameless you can set the window flag equal to QtCore.Qt.FramelessWindowHint

Related

How to restore focus on a widget after hidding then showing the parent widget?

I'm creating a launcher application (like Spotlight/Albert/Gnome-Do). I'm using Python 2.7 and Pyside. Made and used on Windows 10.
It is running in the background and listening to a shortcut with the keyboard (pip install keyboard). When the shortcut is called, a QObject signal calls the show method of my main widget.
My issue is that when the main widget gets hidden by pressing escape or return, next time the widget is shown, the focus will be in the QlineEdit and the user will be able to type its query straight away.
But when the widget is hidden by clicking outside widget (handled by filtering the QEvent WindowDeactivate), the focus won't be on my QLineEdit at next call, which ruins the user experience.
I've tried playing with activateWindow() or raise_(), but it doesn't change anything.
Heree here a simplified example code that shows my problem:
import sys
import keyboard
from PySide.QtCore import *
from PySide.QtGui import *
SHORTCUT = 'Ctrl+space'
class ShortcutThread(QObject):
signal = Signal()
class Launcher(QMainWindow):
def __init__(self, parent=None):
super(Launcher, self).__init__()
self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Popup)
self.resize(500, 50)
self.central_widget = QWidget()
self.setCentralWidget(self.central_widget)
self.layout_ = QHBoxLayout()
self.central_widget.setLayout(self.layout_)
self.search = QLineEdit()
self.layout_.addWidget(self.search)
def eventFilter(self, obj, event):
# Hide dialog when losing focus
if event.type() == QEvent.WindowDeactivate:
self.hide()
return super(Launcher, self).eventFilter(obj, event)
def keyPressEvent(self, e):
# Hide dialog when pressing escape or return
if e.key() in [Qt.Key_Escape, Qt.Key_Return]:
self.hide()
def main():
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
launcher = Launcher()
shortcut = ShortcutThread()
shortcut.signal.connect(launcher.show)
keyboard.add_hotkey(SHORTCUT, shortcut.signal.emit, args=[])
launcher.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
When I call the shortcut (Ctrl+Space here) and click elsewhere, next time I'll call the shortcut, the focus won't be set to the QLineEdit widget.
When the launcher is hidden by hitting return or escape, it does work as expected.

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 implement an always-on-top widget that hides when losing focus

I need to implement a top-level widget (fixed position on screen) that hides whenever the user clicks somewhere else in the desktop, but it should hide gradually, so the widget should still be visible when it happens. To simplify, I want something like the Windows 8 right sidebar, when the user pushes a button, like the Super key it comes up, when clicking somewhere else it fades away, but is still visible in the process.
This is, I want to have an always-on-top window that hides when it loses focus. I have implemented this in pyqt4 but it is not working.
import sys
from PyQt4 import QtGui, QtCore
class Signals(QtCore.QObject):
close = QtCore.pyqtSignal()
class Menu(QtGui.QWidget):
def __init__(self, signals):
super(Menu, self).__init__()
self.signals = signals
def mousePressEvent(self, event):
# Just simplificating the gradual hiding effect for the moment
self.signals.close.emit()
def focusOutEvent(self, event):
print "FocusOut"
self.signals.close.emit()
def main():
app = QtGui.QApplication(sys.argv)
signals = Signals()
signals.close.connect(app.quit)
w = Menu(signals)
w.setWindowFlags( QtCore.Qt.SplashScreen )
w.resize(200, 200)
w.move(0, 0)
w.setWindowTitle('Test')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
However, this is not working. When I click somewhere else the widget won't enter the focusOutEvent. I've also tried installing an eventFilter but since the window is a SplashScreen it won't work.
Any ideas on how to tackle this?

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.

Hide window from taskbar

I'm trying to minimize a window to the tray, but it seems it refuses to hide from the taskbar. I've spent a little time and distilled the problem code down to this. It's not mcuh so I'm wondering if I need something else to hide my app to tray in Windows 7.
import sys, os
from PyQt4 import uic
from PyQt4.QtGui import QMainWindow, QApplication
class MyClass(QMainWindow):
def __init__(self, parent = None):
QMainWindow.__init__(self, parent)
self.ui = uic.loadUi(os.path.join("gui", "timeTrackerClientGUI.ui"), self)
def hideEvent(self, event):
self.hide()
def showEvent(self, event):
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
wnd = MyClass()
wnd.show()
app.exec_()
It seems the application icon does hide but then another one pops up, If I click the taskbar icon multiple times I can get these two icons flickering, looks kind of like this for a splitsecond before the first one hides:
It took a quite ugly hack to get it working but here's the final code if anybody is interested, ph is my platform-specific module, you can use platform.name or similar function instead:
def hideEvent(self, event):
self.hide()
if ph.is_windows():
self.hidden = True
self.setWindowFlags(Qt.ToolTip)
def showEvent(self, event):
if ph.is_windows() and self.hidden:
self.setWindowFlags(Qt.Window)
self.hidden = False
self.show()
calling show/hide in showEvent()/hideEvent() doesn't make sense - those events are the result of show()/hide() calls (and the like), not the trigger. If you want to toggle the window visiblity by clicking the tray icon, try setVisible(!isVisible()) on the widget, if you want to hide the window when the user clicks the window close button try reimplementing closeEvent():
MyMainWindow::closeEvent( QCloseEvent* e ) {
hide();
e->accept();
}
In Python, that is
def closeEvent(self, event):
self.hide()
event.accept()

Categories

Resources