I want to use a dialog together with the SysTrayIcon.
The reason for the app is running in a systray, and show the dialog when you press the icon button. The app will close entirely just from the menu option.
Problem to solve: when lose focus (eg:clicking outisde) he dialog should hide even if the dialog had a button to display a modal window
Please test the sample code, as it illustrates everything.
from PySide2.QtWidgets import *
import sys
message = '''
This is a dialog we wanna use together with the SysTrayIcon
The reason of the app is running in a systray, and show the
dialog when you press the icon button. The app will close
entirely from the menu option, explained below.
Clicking SysTray icon use:
1) Click left and right in icon it shows the app
and you can interact with it. If you click again it toggles
show/hide the app.
2) With Middle Click a menu shows up, and dialog hides.
From this menu you can close the app entirely.
PROBLEM TO SOLVE:
When lose focus(eg:clicking outisde) he dialog should hide
Even if the dialog had a button to display a modal window
'''
class LauncherDialog(QDialog):
def __init__(self, x=None, y=None, parent=None):
super(LauncherDialog, self).__init__(parent)
my_lbl = QLabel(message)
my_layout = QVBoxLayout()
my_layout.addWidget(my_lbl)
my_layout.setMargin(0)
self.setLayout(my_layout)
self.setMinimumHeight(630)
self.setMinimumWidth(360)
self.setWindowTitle("Launcher")
if x is not None and y is not None:
self.move(x, y)
class SystrayLauncher(object):
def __init__(self):
# Create the icon
w = QWidget() #just to get the style(), haven't seen other way
icon = w.style().standardIcon(QStyle.SP_MessageBoxInformation)
# Create the tray
self.tray = QSystemTrayIcon()
self.tray.setIcon(icon)
self.tray.setVisible(True)
self.tray_pos = self.tray.geometry()
left = self.tray_pos.left()
height = self.tray_pos.height()
self.menu = QMenu()
self.action = QAction("&Quit", None, triggered=QApplication.instance().quit)
self.tray.setContextMenu(self.menu)
self.launcher_window = LauncherDialog(x=left, y=height)
self.tray.activated.connect(self.handleLeftRightMiddleClick)
def handleLeftRightMiddleClick(self, signal):
if signal == QSystemTrayIcon.ActivationReason.MiddleClick:
self.menu.addAction(self.action) #show menu
self.launcher_window.hide()
elif signal == QSystemTrayIcon.ActivationReason.Trigger: #left / right clicks
self.menu.removeAction(self.action) #hide menu
if self.launcher_window.isHidden():
self.launcher_window.show()
else:
self.launcher_window.hide()
else: #doubleclick
pass
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False)
sl = SystrayLauncher()
sys.exit(app.exec_())
Related
I have this code:
def initUI(self):
mainLayout = QVBoxLayout()
# Temporary: Dialog Testing Button #
self.buttonTest = button('Dialog Testing')
self.buttonTest.clicked.connect(self.TestFile)
mainLayout.addWidget(self.buttonTest)
self.setLayout(mainLayout)
# Connects to dialogTesting Button
def dialogMessage(self, message):
dialog = QMessageBox(self)
dialog.setWindowTitle('Sample Text')
dialog.setText(message)
dialog.show()
# Connects with dialogMessage class.
def TestFile(self):
self.dialogMessage("When will non-OK buttons appear?")
return
I get results like this:
How can I change what buttons appear in the popup?
You can change the buttons by using an argument for the buttons parameter when you instantiate a QMessageBox. You use the Standard Button constants. Here is a silly example:
dialog = QMessageBox(self, buttons=QMessageBox.Ok+QMessageBox.Save+QMessageBox.Yes )
If you don't like the choices for standard buttons, you can make your own using addButton. This answer shows how to use addButton.
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_())
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.
Here is my code
app = QApplication(sys.argv)
loginWindow = loadUi('~/mainwindow.ui')
okconnect = loadUi('~/okconnect.ui')
def prueba2(test):
print(test + " Testing2")
def prueba(test):
print (test)
okconnect.show()
okconnect.getvmsButton.clicked.connect(lambda: prueba2(test))
loginWindow.show()
loginWindow.connectButton.clicked.connect(lambda: prueba("test"))
sys.exit(app.exec_())
When I press "connect" button, a new window is opened, with a button, "getvmsButton", when I press this button, its show in console the print line in def prueba2(test), but if I close the window, and then I click again in connect button, the window open again and "getvmsButton" is pressed again, the console show 2 messages instead of 1. If I repeat the process, more messages are showed.
What should I change to show only one messages when I close and open several times the window?
When you close your GUI by 'x' doesn't mean you disconnected the button click signal, therefore, every time you click the 'connect' button, the loginWindow will create a new button.clicked event and keep the old ones. A quick solution to this is to define a close-event for the 'x' clicks. Here provided is just a way to close the old signal events.
from PyQt5.QtWidgets import QWidget
OKConnect(QWidget):
def __init__(self):
super().__init__()
self.okconnect = loadUi('~/okconnect.ui')
self.okconnect.closeEvent = self.closeEvent
def closeEvent(self, event):
print("testing2 closing")
# to close the old getvmsButton signals
self.okconnect.getvmsButton.disconnect()
# Here below is exactly your code
app = QApplication(sys.argv)
loginWindow = loadUi('~/mainwindow.ui')
okconnect = OKConnect()
def prueba2(test):
print(test + " Testing2")
def prueba(test):
print (test)
okconnect.show()
okconnect.getvmsButton.clicked.connect(lambda: prueba2(test))
loginWindow.show()
loginWindow.connectButton.clicked.connect(lambda: prueba("test"))
sys.exit(app.exec_())
I am not sure about your .ui files, the code may not work on yours.
I showed another window to the user when he clicks 'Go to work' and hide current window. But when I hide current window, there's no application icon in the taskbar. How can I do what I want?
I want to show new window and hide current, but the icon in the taskbar is not showing.
def start(self):
window = MainWindow(self)
window.show()
self.hide()
Switch between self.hide() and self.showMinimized()
def start(self):
window = MainWindow(self)
window.show()
self.showMinimized()