From 2f29ada2936d66ac6b46709286e26023c9137378 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 25 Nov 2018 15:28:22 -0500 Subject: [PATCH 1/3] Double click detection for SystemTray. Returned as an event --- PySimpleGUIQt/PySimpleGUIQt.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/PySimpleGUIQt/PySimpleGUIQt.py b/PySimpleGUIQt/PySimpleGUIQt.py index 04f0e2bd..a93b8ced 100644 --- a/PySimpleGUIQt/PySimpleGUIQt.py +++ b/PySimpleGUIQt/PySimpleGUIQt.py @@ -200,10 +200,13 @@ ThisRow = 555666777 # magic number MESSAGE_BOX_LINE_WIDTH = 60 # "Special" Key Values.. reserved +# Events that are pre-defined # Key representing a Read timeout TIMEOUT_KEY = '__TIMEOUT__' # Key indicating should not create any return values for element WRITE_ONLY_KEY = '__WRITE ONLY__' +EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED = '__DOUBLE_CLICKED__' +EVENT_SYSTEM_TRAY_MESSAGE_CLICKED = '__MESSAGE_CLICKED__' # Meny key indicator character / string MENU_KEY_SEPARATOR = '::' @@ -2705,7 +2708,7 @@ class SystemTray: self.TrayIcon.setToolTip(str(self.Tooltip)) self.TrayIcon.messageClicked.connect(self.messageClicked) - + self.TrayIcon.activated.connect(self.doubleClicked) self.TrayIcon.setContextMenu(qmenu) @@ -2715,10 +2718,15 @@ class SystemTray: # callback function when message is clicked def messageClicked(self): - self.MenuItemChosen = '_MESSAGE_CLICKED_' + self.MenuItemChosen = EVENT_SYSTEM_TRAY_MESSAGE_CLICKED self.App.exit() + def doubleClicked(self, reason): + if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): + self.MenuItemChosen = EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED + self.App.exit() + def Read(self, timeout=None): ''' Reads the context menu From ddfae6131a000e2c14128297e03a188b8824eff9 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 25 Nov 2018 16:08:48 -0500 Subject: [PATCH 2/3] New ICON_ACTIVATED event for Tray Icon, better Tray/Window interaction management --- PySimpleGUIQt/PySimpleGUIQt.py | 72 +++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/PySimpleGUIQt/PySimpleGUIQt.py b/PySimpleGUIQt/PySimpleGUIQt.py index a93b8ced..13699d0a 100644 --- a/PySimpleGUIQt/PySimpleGUIQt.py +++ b/PySimpleGUIQt/PySimpleGUIQt.py @@ -6,32 +6,50 @@ import textwrap import pickle import base64 import calendar -try: - from PySide2.QtWidgets import QApplication, QLabel, QWidget, QLineEdit, QComboBox, QFormLayout, QVBoxLayout, \ - QHBoxLayout, QListWidget, QDial, QTableWidget - from PySide2.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog, QAbstractItemView - from PySide2.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog, QTableWidget, QTabWidget, QTabBar, QTreeWidget, QTreeWidgetItem, QLayout, QTreeWidgetItemIterator, QProgressBar - from PySide2.QtWidgets import QTableWidgetItem, QGraphicsView, QGraphicsScene, QGraphicsItemGroup, QMenu, QMenuBar, QAction, QSystemTrayIcon - from PySide2.QtGui import QPainter, QPixmap, QPen, QColor, QBrush, QPainterPath, QFont, QImage, QIcon - from PySide2.QtCore import Qt,QProcess, QEvent - import PySide2.QtGui as QtGui - import PySide2.QtCore as QtCore - import PySide2.QtWidgets as QtWidgets - using_pyqt5 = False -except: + +FORCE_PYQT5 = True + +if not FORCE_PYQT5: + try: + from PySide2.QtWidgets import QApplication, QLabel, QWidget, QLineEdit, QComboBox, QFormLayout, QVBoxLayout, \ + QHBoxLayout, QListWidget, QDial, QTableWidget + from PySide2.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog, QAbstractItemView + from PySide2.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog, QTableWidget, QTabWidget, QTabBar, QTreeWidget, QTreeWidgetItem, QLayout, QTreeWidgetItemIterator, QProgressBar + from PySide2.QtWidgets import QTableWidgetItem, QGraphicsView, QGraphicsScene, QGraphicsItemGroup, QMenu, QMenuBar, QAction, QSystemTrayIcon + from PySide2.QtGui import QPainter, QPixmap, QPen, QColor, QBrush, QPainterPath, QFont, QImage, QIcon + from PySide2.QtCore import Qt,QProcess, QEvent + import PySide2.QtGui as QtGui + import PySide2.QtCore as QtCore + import PySide2.QtWidgets as QtWidgets + using_pyqt5 = False + except: + from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QLineEdit, QComboBox, QFormLayout, QVBoxLayout, \ + QHBoxLayout, QListWidget, QDial, QTableWidget + from PyQt5.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog, QAbstractItemView + from PyQt5.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog, QTableWidget, QTabWidget, QTabBar, QTreeWidget, QTreeWidgetItem, QLayout, QTreeWidgetItemIterator, QProgressBar + from PyQt5.QtWidgets import QTableWidgetItem, QGraphicsView, QGraphicsScene, QGraphicsItemGroup, QMenu, QMenuBar, QAction, QSystemTrayIcon + from PyQt5.QtGui import QPainter, QPixmap, QPen, QColor, QBrush, QPainterPath, QFont, QImage, QIcon + from PyQt5.QtCore import Qt,QProcess, QEvent + import PyQt5.QtGui as QtGui + import PyQt5.QtCore as QtCore + import PyQt5.QtWidgets as QtWidgets + using_pyqt5 = True +else: from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QLineEdit, QComboBox, QFormLayout, QVBoxLayout, \ QHBoxLayout, QListWidget, QDial, QTableWidget - from PyQt5.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, QDialog, QAbstractItemView - from PyQt5.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog, QTableWidget, QTabWidget, QTabBar, QTreeWidget, QTreeWidgetItem, QLayout, QTreeWidgetItemIterator, QProgressBar - from PyQt5.QtWidgets import QTableWidgetItem, QGraphicsView, QGraphicsScene, QGraphicsItemGroup, QMenu, QMenuBar, QAction + from PyQt5.QtWidgets import QSlider, QCheckBox, QRadioButton, QSpinBox, QPushButton, QTextEdit, QMainWindow, \ + QDialog, QAbstractItemView + from PyQt5.QtWidgets import QSpacerItem, QFrame, QGroupBox, QTextBrowser, QPlainTextEdit, QButtonGroup, QFileDialog, \ + QTableWidget, QTabWidget, QTabBar, QTreeWidget, QTreeWidgetItem, QLayout, QTreeWidgetItemIterator, QProgressBar + from PyQt5.QtWidgets import QTableWidgetItem, QGraphicsView, QGraphicsScene, QGraphicsItemGroup, QMenu, QMenuBar, \ + QAction, QSystemTrayIcon from PyQt5.QtGui import QPainter, QPixmap, QPen, QColor, QBrush, QPainterPath, QFont, QImage, QIcon - from PyQt5.QtCore import Qt,QProcess, QEvent + from PyQt5.QtCore import Qt, QProcess, QEvent import PyQt5.QtGui as QtGui import PyQt5.QtCore as QtCore import PyQt5.QtWidgets as QtWidgets using_pyqt5 = True - """ The QT version if PySimpleGUI. Still being developed. Very limited features. Been in development for less than 2 days so don't expect much!! @@ -206,6 +224,7 @@ TIMEOUT_KEY = '__TIMEOUT__' # Key indicating should not create any return values for element WRITE_ONLY_KEY = '__WRITE ONLY__' EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED = '__DOUBLE_CLICKED__' +EVENT_SYSTEM_TRAY_ICON_ACTIVATED = '__ACTIVATED__' EVENT_SYSTEM_TRAY_MESSAGE_CLICKED = '__MESSAGE_CLICKED__' # Meny key indicator character / string @@ -2672,13 +2691,13 @@ class SystemTray: self.Menu = menu self.TrayIcon = None self.Shown = False - self.MenuItemChosen = None + self.MenuItemChosen = TIMEOUT_KEY self.Tooltip = tooltip global _my_windows if _my_windows.QTApplication is None: - _my_windows.QTApplication = QApplication() + _my_windows.QTApplication = QApplication(sys.argv) self.App = _my_windows.QTApplication self.QWidget = QWidget() @@ -2723,9 +2742,13 @@ class SystemTray: def doubleClicked(self, reason): - if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): + # print(reason) + if reason == QSystemTrayIcon.DoubleClick: self.MenuItemChosen = EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED self.App.exit() + if reason == QSystemTrayIcon.Trigger: + self.MenuItemChosen = EVENT_SYSTEM_TRAY_ICON_ACTIVATED + self.App.exit() def Read(self, timeout=None): ''' @@ -2738,13 +2761,15 @@ class SystemTray: self.TrayIcon.show() if timeout is None: self.App.exec_() + else: + self.App.processEvents() else: if timeout is None: self.App.exec_() else: self.App.processEvents() item = self.MenuItemChosen - self.MenuItemChosen = None + self.MenuItemChosen = TIMEOUT_KEY return item @@ -3068,6 +3093,9 @@ class Window: else: if not self.XFound and self.Timeout != 0 and self.Timeout is not None and self.ReturnValues[0] is None: # Special Qt case because returning for no reason so fake timeout self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout + elif not self.XFound: # TODO HIGHLY EXPERIMENTAL... added due to tray icon interaction + # print("*** Faking timeout ***") + self.ReturnValues = self.TimeoutKey, self.ReturnValues[1] # fake a timeout return self.ReturnValues def _ReadNonBlocking(self): From adb34a637138e5c309f911a4c889ab2a517100e0 Mon Sep 17 00:00:00 2001 From: MikeTheWatchGuy Date: Sun, 25 Nov 2018 16:30:59 -0500 Subject: [PATCH 3/3] Release 0.16.0 --- PySimpleGUIQt/PySimpleGUIQt.py | 2 +- PySimpleGUIQt/readme.md | 38 ++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/PySimpleGUIQt/PySimpleGUIQt.py b/PySimpleGUIQt/PySimpleGUIQt.py index 13699d0a..8da0d0a1 100644 --- a/PySimpleGUIQt/PySimpleGUIQt.py +++ b/PySimpleGUIQt/PySimpleGUIQt.py @@ -7,7 +7,7 @@ import pickle import base64 import calendar -FORCE_PYQT5 = True +FORCE_PYQT5 = False if not FORCE_PYQT5: try: diff --git a/PySimpleGUIQt/readme.md b/PySimpleGUIQt/readme.md index dda35e6b..189f9c6a 100644 --- a/PySimpleGUIQt/readme.md +++ b/PySimpleGUIQt/readme.md @@ -154,7 +154,7 @@ These Elements are "complete" (a relative term... more are more complete than ot Notable MISSING features at the moment include: * Graphs Element Methods - erasing, draw arc, etc -# New PySimpleGUI Features +# New PySimpleGUI Features only in Qt There are a number of new features that are only available in PySimpleGUIQt. These include: * ButtonMenu Element @@ -208,7 +208,7 @@ while True: ``` ## SystemTray Methods -### Read - Read the context menu +### Read - Read the context menu or check for events ```python def Read(timeout=None): @@ -219,6 +219,15 @@ def Read(timeout=None): ''' ``` +#### Read special return values + +In addition to Menu Items, the Read call can return several special values. They include: + +EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED - Tray icon was double clicked +EVENT_SYSTEM_TRAY_ICON_ACTIVATED - Tray icon was single clicked +EVENT_SYSTEM_TRAY_MESSAGE_CLICKED - a message balloon was clicked +TIMEOUT_KEY is returned if no events are available if the timeout value is set in the Read call + ### Hide Hides the icon @@ -254,6 +263,17 @@ def ShowMessage(title, message, filename=None, data=None, data_base64=None, time ''' ``` +## Menus with Keys + +PySimpleGUIQt offers the ability to add a key to your menu items. To do so, you add :: and the key value to the end of your menu definition. + +`menu_def = ['File', ['Hide::key', '&Open::key', '&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]` + +The menu definition adds a key "key" to the menu entries Hide and Open. + +If you want to change the separator characters from :: top something else,change the variable `MENU_KEY_SEPARATOR` + +When a menu item has a key and it is chosen, then entire string is returned. If Hide were selected, then Hide::key would be returned from the Read. Note that the shortcut character & is NOT returned from Reads. # Release Notes: @@ -335,6 +355,20 @@ Border Depth for all elements that support it (inputs, slider, table, tree, etc) Fix for Element padding done incorrectly!! Sorry about this one +### 0.16.0 24-Nov-2018 + +Easier forcing to use PyQt5 for testing +Predefined events for Tray Icons +* Double Clicked +* Icon Activated +* Message Clicked +* Timeout key for polling + +Tray icon tooltip +Menu keys with programmable separator +Better element padding hierarchy +Menubar now returns values as does the ButtonMenu + # Design