Release 0.18.0 Qt

This commit is contained in:
MikeTheWatchGuy 2018-11-26 23:05:07 -05:00
parent c8556d517f
commit 0c651c186c
4 changed files with 353 additions and 73 deletions

View File

@ -11,12 +11,11 @@
logo = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAtCAMAAADbYcjNAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAADBpmDFqmDBqmTFqmjJrmzJsmzJsnDNtnTRrmjVtmjZsmzRunTRunjVvnzdwnjVwnzpxnzVwoDZxoTdyojlyoThzozhzpDh0pDp1pjp2pj51ozt3qDt4qDx4qDx5qj16qj57rD58rT98rkF1oEB4pUB4pkR6pkJ6qEN8q0B9rUB9rkB+rkV7qUZ8qUp9p0x+p0h/rEB+sEeArkqArEqBr0uCrkKAsUKAskOCs0OCtESCtEWEtkaFuEaGuE2FsUiHukiIukmJvEmKvEqLvkuMvk2KuUyLvEyMv1OErVWDqlWHr1qHrVaIsVCMvFSPvV2LsViOuVSQvmyUtmyXuXKbvXefv3ugv06NwE6OwFmUwl2XxGScyGmbw22hynikxnmmyv/UO//UPP/VPf/UPv/VP//UQP/VQf/VQv/WQP/WQf/WQv/WQ//XRP/WRf/WSf/YRf/YRv/YR//YSP/ZSf/ZSv/aS//aTP/aTf/bTv/YUf/ZUv/bUP/cUP/cUv/dVP/dVv/eVv/bW//dWf/cWv/eWP/fWv/dXf/fXf/eXv/cYP/fYP/dZP/dZv/eZf/fZv/eaP/gW//gXP/gXv/gYP/iYf/iYv/hZP/jZP/iZv/kZv/jaf/ja//kaP/lav/kbP/lb//mbP/mbv/ncP/mcv/iff/ocv/odP/odv/oeP/of//qf4GnxYOox4SoxYSpx4asyo+ux4isyouuyouvzIyuyYyvy4yvzI6wy46wzIyz0pCuyJSxyZWyy5u3zZ24zpW30pG52J250J+60aC60KS90aDC3a3E163F2K3F2bPI2bvO3rzP3qvJ4LHN4rnR5P/qgf/qgv/qiP/sif/sjf/sj//olf/ql//ulv/omf/qnv/tnP/qoP/ro//qpP/sov/upf/tqP/uqP/vrf/vrv/us//wpP/wpv/xrf/wsP/wsv/ys//xtP/ytf/ytv/zuf/zuv/0vP/0vsDS38XZ6cnb6f/xw//zwv/yxf/1w//zyP/1yf/2zP/3z//30wAAAM55ho4AAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAABcQAAAXEAEYYRHbAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjFjKpxLAAADnElEQVRIS62OeVhUZRSHw6IosQzGBgoIszKXqGylghKwbHErNVPbEFQQTXYogzZtt2SmiSEHnBHJPdM2Ldv3xbW91Pay0tT29dc53znfvTP809Pz9P7x3Xvu8773fHvhP/M/JBce0GX/8/WduX3ipDt/1nclNrk4TnhR5y1FVzHX/KqzISY5Uou4uLVm/sgEzF1mFqKTruozL9D8gfrM1aIwbvJaF7WFJ4FJqgtb1XOTN1R1eBqYrLbwvpo2+SF2B/NEbFNY+IWoNum6t0UD4imgTO3CCROKiqaJqsnx8fHx+7ho/RBALsnFxcUlJSWlNxpXkkv2I/a1uPnLWEA626WlU6aUlf3uJomJiccdlJDAoQvFBwPsklw2deq06dNnO8nIbj3oHE4hkWDQ7HVcSzLb5eXlFRWVTtKj+/P8OJDojfO6Wahfi3uMW1FZWVVVVV39jk2Skl6RR9JhwOjunDJUPYfZ1q6uqampvcUmWZ4sOpcnJ9Pj8WQqHYAZ4tbW1tXV19ffbJNXPZ6sUUM8nqOBRzweT7LDBQDZdcYmZlz3rk2wNCUlxes9iXYcwlBmOAq4W12moeE2liXBg9QcA6yiB+P1eqk8FtgmdoOh8SbjaoJlqacAj6ZqYqBffCJyo+GGO0S1CVYDKw8VUg0nAJ87NnOrmk4CPJYmSNeHdjQ2Xm/kmcx9qkUlKzKU9PT0tLSTaYeVZ84i/KpFJQ9nZmYermRknAh8qu6sOU1EUDXCJit6UuJwFu1gm+WmJp/PR7f6xr9NVE2eOYLoaeEdVvb5/H7/XOC7QCDwoXE16d+L4IzpC3xmZLb99wYC9wPbm5mP2ZVkRH9DP0OvK/CLcUkmmpvn0Y5gsKWlJRRykwEOXAJBI5NNBNtoRyhEJxa3bnKS005ltBoAqGz+3E47Qq2tO9gLR+jQJAbaItdgWhdje1tbOPwHe5GFdEhyuiE7O1sTkQ1t4fZwOBKJbCTt6/lfOsllZ0TzE9rZpV8bORKZz2z46q2ODpYlwZmCJFfiTyuL3WHZzK4ma7QRgJ2dZcG4mmBoriEnJ4eSc4BvO9vMe0a1CQZKIwwDdqkWxRIxnWT9QOJsITd3KN1NRRc1nQTrzs3L40y4CNitprLwbxXdBD/mM3nCWPoQs+cBkYioBLi8oMBk+flcAHtUJ942HwwxCd4cM2hQATFO5+81WPSbfmBiE+Cl8ZcOHvusDsBfG+hKm/foJHRO/hXgH831bVAP1oP5AAAAAElFTkSuQmCC'
import PySimpleGUIQt as sg
# create and then hide the tray icon
menu_def = ['My Menu Def', ['&Restore', '&Open', '&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]
tray = sg.SystemTray('Title', menu=menu_def, data_base64=logo)
menu_def = ['My Menu Def', ['&Restore', '&Open','---', '&Message','&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]
tray = sg.SystemTray(menu=menu_def, data_base64=logo)
tray.Hide()
# create the window
@ -45,8 +44,7 @@ while True: # the event loop
tray.UnHide()
tray_visible = True
window_visible = False
elif event == 'Message':
tray.ShowMessage('Title of message', 'This is a tray message')
# do tray icon stuff. Tray events will cause the window.Read call to return with a Timeout event
if tray_visible:
menu_item = tray.Read()
@ -67,6 +65,7 @@ while True: # the event loop
window.UnHide()
else: # some other tray command was received
print('Menu item %s'%menu_item)
if menu_item == 'Exit':
if menu_item == 'Exit' or menu_item == sg.EVENT_SYSTEM_TRAY_MESSAGE_CLICKED:
break
elif menu_item == 'Message':
tray.ShowMessage('Title of message', 'This is a tray message\nNote the icon shown in the message too\nIf you click the message it will close the program')

View File

@ -0,0 +1,127 @@
#!/usr/bin/env python
import PySimpleGUIQt as sg
import re
# Import requests (to download the page)
import requests
# Import BeautifulSoup (to parse what we download)
from bs4 import BeautifulSoup
# search github for total open issues and Issue Number of first issue
def get_num_issues():
url = "https://github.com/MikeTheWatchGuy/PySimpleGUI/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc"
# set the headers like we are a browser,
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
# download the page
response = requests.get(url, headers=headers)
# parse the downloaded homepage and grab all text,
soup = BeautifulSoup(response.text, "lxml")
# look for phrase "XXX Open"
findopen = re.compile(r"\d+ Open")
# get number of open issues
number_open_string = findopen.search(str(soup)).group(0)
num_open_issues = number_open_string[0:number_open_string.index(' ')]
# find the first issue in the list by earing for "issue-id-XXXX"
find_first_issue = re.compile(r'issue-id-\d+')
first_issue_string = find_first_issue.search(str(soup)).group(0)
first_issue = first_issue_string[9:]
return num_open_issues, first_issue
def gui():
sg.ChangeLookAndFeel('Topanga')
sg.SetOptions(border_width=0)
layout = [
[sg.T('GitHub Issues Watcher' + 5 * ' ', click_submits=True, key='GitHub'),
sg.Button('', size=(25,25),
image_data=red_x,
key='_quit_',button_color=(sg.LOOK_AND_FEEL_TABLE['Topanga']['TEXT'],sg.LOOK_AND_FEEL_TABLE['Topanga']['BACKGROUND']),
tooltip='Closes window')],
[sg.T('', key='_status_', size=(12, 1))],
[sg.T('', key='_numissues_', size=(20, 1))],
]
window = sg.Window('Issue watcher',
no_titlebar=True,
grab_anywhere=True,
keep_on_top=True,
alpha_channel=.8, # dim the lights a little
location=(2360,310), # locate in upper right corner of screen
).Layout(layout).Finalize()
window.Refresh()
status_elem = window.FindElement('_status_')
issues_elem = window.FindElement('_numissues_')
initial_issue_count, initial_first_issue = get_num_issues()
seconds = 0
poll_frequncy = 1000
while True:
event, values = window.Read(timeout=poll_frequncy)
if event in ('_quit_', None):
break
if seconds % 60 == 0 or event.startswith('GitHub'): # Every 60 seconds read GitHub
status_elem.Update('Reading...')
window.Refresh()
issues, first_issue = get_num_issues()
issues_elem.Update('{} Issues. {} is first issue'.format(issues, initial_first_issue))
window.Refresh()
# if something changed, then make a popup
if issues != initial_issue_count or first_issue != initial_first_issue:
sg.PopupNoWait('Issues changed on GitHub ', 'First issue # is {}'.format(first_issue), background_color='red', keep_on_top=True)
initial_issue_count = issues
initial_first_issue = first_issue
status_elem.Update('')
else:
status_elem.Update('.' if seconds%2 else '') # blink a '.' every 2 seconds so know still running
seconds += poll_frequncy/1000
window.Close()
def system_tray():
menu_def = ['Root',
['E&xit']]
tray = sg.SystemTray(data_base64=logo, tooltip='GitHub Issue Watcher')
# tray.Hide()
initial_issue_count, initial_first_issue = get_num_issues()
# The Event Loop runs every 5000ms
poll_frequncy = 5000
seconds = 0
while True:
menu_item = tray.Read(timeout=5000)
if menu_item == 'Exit':
break
if menu_item == 'Run GUI':
gui()
if seconds % 12 == 0: # Every 60 seconds read GitHub
issues, first_issue = get_num_issues()
menu_def = ['root',
['{} Issues'.format(issues), '{} First Issue'.format(first_issue), '---', '&Run GUI', '&Refresh', 'E&xit']]
tray.Update(menu_def, tooltip='{} First Issue'.format(first_issue))
# if something changed, then make a popup
if issues != initial_issue_count or first_issue != initial_first_issue:
sg.PopupNoWait('Issues changed on GitHub ', 'First issue # is {}'.format(first_issue), background_color='red', keep_on_top=True)
initial_issue_count = issues
initial_first_issue = first_issue
if menu_item in('Refresh', sg.EVENT_SYSTEM_TRAY_ICON_DOUBLE_CLICKED):
issues, first_issue = get_num_issues()
tray.ShowMessage('Issue', '{} Issues\n{} First Issue'.format(issues, first_issue), messageicon=sg.SYSTEM_TRAY_MESSAGE_ICON_INFORMATION, )
tray.Update(data_base64=red_x)
seconds += poll_frequncy/1000
red_x = b"R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
logo = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAtCAMAAADbYcjNAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAADBpmDFqmDBqmTFqmjJrmzJsmzJsnDNtnTRrmjVtmjZsmzRunTRunjVvnzdwnjVwnzpxnzVwoDZxoTdyojlyoThzozhzpDh0pDp1pjp2pj51ozt3qDt4qDx4qDx5qj16qj57rD58rT98rkF1oEB4pUB4pkR6pkJ6qEN8q0B9rUB9rkB+rkV7qUZ8qUp9p0x+p0h/rEB+sEeArkqArEqBr0uCrkKAsUKAskOCs0OCtESCtEWEtkaFuEaGuE2FsUiHukiIukmJvEmKvEqLvkuMvk2KuUyLvEyMv1OErVWDqlWHr1qHrVaIsVCMvFSPvV2LsViOuVSQvmyUtmyXuXKbvXefv3ugv06NwE6OwFmUwl2XxGScyGmbw22hynikxnmmyv/UO//UPP/VPf/UPv/VP//UQP/VQf/VQv/WQP/WQf/WQv/WQ//XRP/WRf/WSf/YRf/YRv/YR//YSP/ZSf/ZSv/aS//aTP/aTf/bTv/YUf/ZUv/bUP/cUP/cUv/dVP/dVv/eVv/bW//dWf/cWv/eWP/fWv/dXf/fXf/eXv/cYP/fYP/dZP/dZv/eZf/fZv/eaP/gW//gXP/gXv/gYP/iYf/iYv/hZP/jZP/iZv/kZv/jaf/ja//kaP/lav/kbP/lb//mbP/mbv/ncP/mcv/iff/ocv/odP/odv/oeP/of//qf4GnxYOox4SoxYSpx4asyo+ux4isyouuyouvzIyuyYyvy4yvzI6wy46wzIyz0pCuyJSxyZWyy5u3zZ24zpW30pG52J250J+60aC60KS90aDC3a3E163F2K3F2bPI2bvO3rzP3qvJ4LHN4rnR5P/qgf/qgv/qiP/sif/sjf/sj//olf/ql//ulv/omf/qnv/tnP/qoP/ro//qpP/sov/upf/tqP/uqP/vrf/vrv/us//wpP/wpv/xrf/wsP/wsv/ys//xtP/ytf/ytv/zuf/zuv/0vP/0vsDS38XZ6cnb6f/xw//zwv/yxf/1w//zyP/1yf/2zP/3z//30wAAAM55ho4AAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAABcQAAAXEAEYYRHbAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjFjKpxLAAADnElEQVRIS62OeVhUZRSHw6IosQzGBgoIszKXqGylghKwbHErNVPbEFQQTXYogzZtt2SmiSEHnBHJPdM2Ldv3xbW91Pay0tT29dc53znfvTP809Pz9P7x3Xvu8773fHvhP/M/JBce0GX/8/WduX3ipDt/1nclNrk4TnhR5y1FVzHX/KqzISY5Uou4uLVm/sgEzF1mFqKTruozL9D8gfrM1aIwbvJaF7WFJ4FJqgtb1XOTN1R1eBqYrLbwvpo2+SF2B/NEbFNY+IWoNum6t0UD4imgTO3CCROKiqaJqsnx8fHx+7ho/RBALsnFxcUlJSWlNxpXkkv2I/a1uPnLWEA626WlU6aUlf3uJomJiccdlJDAoQvFBwPsklw2deq06dNnO8nIbj3oHE4hkWDQ7HVcSzLb5eXlFRWVTtKj+/P8OJDojfO6Wahfi3uMW1FZWVVVVV39jk2Skl6RR9JhwOjunDJUPYfZ1q6uqampvcUmWZ4sOpcnJ9Pj8WQqHYAZ4tbW1tXV19ffbJNXPZ6sUUM8nqOBRzweT7LDBQDZdcYmZlz3rk2wNCUlxes9iXYcwlBmOAq4W12moeE2liXBg9QcA6yiB+P1eqk8FtgmdoOh8SbjaoJlqacAj6ZqYqBffCJyo+GGO0S1CVYDKw8VUg0nAJ87NnOrmk4CPJYmSNeHdjQ2Xm/kmcx9qkUlKzKU9PT0tLSTaYeVZ84i/KpFJQ9nZmYermRknAh8qu6sOU1EUDXCJit6UuJwFu1gm+WmJp/PR7f6xr9NVE2eOYLoaeEdVvb5/H7/XOC7QCDwoXE16d+L4IzpC3xmZLb99wYC9wPbm5mP2ZVkRH9DP0OvK/CLcUkmmpvn0Y5gsKWlJRRykwEOXAJBI5NNBNtoRyhEJxa3bnKS005ltBoAqGz+3E47Qq2tO9gLR+jQJAbaItdgWhdje1tbOPwHe5GFdEhyuiE7O1sTkQ1t4fZwOBKJbCTt6/lfOsllZ0TzE9rZpV8bORKZz2z46q2ODpYlwZmCJFfiTyuL3WHZzK4ma7QRgJ2dZcG4mmBoriEnJ4eSc4BvO9vMe0a1CQZKIwwDdqkWxRIxnWT9QOJsITd3KN1NRRc1nQTrzs3L40y4CNitprLwbxXdBD/mM3nCWPoQs+cBkYioBLi8oMBk+flcAHtUJ942HwwxCd4cM2hQATFO5+81WPSbfmBiE+Cl8ZcOHvusDsBfG+hKm/foJHRO/hXgH831bVAP1oP5AAAAAElFTkSuQmCC'
system_tray()

View File

@ -113,6 +113,7 @@ DEFAULT_DEBUG_WINDOW_SIZE = (800, 400)
DEFAULT_WINDOW_LOCATION = (None, None)
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
DEFAULT_TOOLTIP_TIME = 400
DEFAULT_TOOLTIP_OFFSET = (20,-20)
#################### COLOR STUFF ####################
BLUES = ("#082567", "#0A37A3", "#00345B")
PURPLES = ("#480656", "#4F2398", "#380474")
@ -2684,21 +2685,19 @@ class ErrorElement(Element):
# Tray CLASS #
# ------------------------------------------------------------------------- #
class SystemTray:
def __init__(self, title, filename=None, menu=None, data=None, data_base64=None, tooltip=None):
def __init__(self, menu=None, filename=None, data=None, data_base64=None, tooltip=None):
'''
SystemTray - create an icon in the system tray
:param title:
:param filename:
:param menu:
:param data:
:param data_base64:
:param menu: Menu definition
:param filename: filename for icon
:param data: in-ram image for icon
:param data_base64: basee-64 data for icon
:param tooltip: tooltip string
'''
self.Title = title
self.Menu = menu
self.TrayIcon = None
self.Shown = False
self.MenuItemChosen = TIMEOUT_KEY
self.Tooltip = tooltip
global _my_windows
@ -2725,16 +2724,17 @@ class SystemTray:
return
self.TrayIcon = QSystemTrayIcon(qicon)
qmenu = QMenu()
qmenu.setTitle(self.Menu[0])
AddTrayMenuItem(qmenu, self.Menu[1], self)
if self.Menu is not None:
qmenu = QMenu()
qmenu.setTitle(self.Menu[0])
AddTrayMenuItem(qmenu, self.Menu[1], self)
self.TrayIcon.setContextMenu(qmenu)
if self.Tooltip is not None:
self.TrayIcon.setToolTip(str(self.Tooltip))
if tooltip is not None:
self.TrayIcon.setToolTip(str(tooltip))
self.TrayIcon.messageClicked.connect(self.messageClicked)
self.TrayIcon.activated.connect(self.doubleClicked)
self.TrayIcon.setContextMenu(qmenu)
self.TrayIcon.show()
@ -2766,19 +2766,22 @@ class SystemTray:
if not self.Shown:
self.Shown = True
self.TrayIcon.show()
if timeout is None:
self.App.exec_()
else:
self.App.processEvents()
if timeout is None:
self.App.exec_()
elif timeout == 0:
self.App.processEvents()
else:
if timeout is None:
self.App.exec_()
else:
self.App.processEvents()
self.timer = start_systray_read_timer(self, timeout)
self.App.exec_()
if self.timer:
stop_timer(self.timer)
item = self.MenuItemChosen
self.MenuItemChosen = TIMEOUT_KEY
return item
def timer_timeout(self):
self.App.exit() # kick the users out of the mainloop
def Hide(self):
self.TrayIcon.hide()
@ -2823,7 +2826,52 @@ class SystemTray:
return self
def Close(self):
self.App.quit()
'''
:return:
'''
self.TrayIcon.Hide()
# Don't close app because windows could be depending on it
# self.App.quit()
def Update(self, menu=None, tooltip=None,filename=None, data=None, data_base64=None,):
'''
Updates the menu, tooltip or icon
:param menu: menu defintion
:param tooltip: string representing tooltip
:param filename: icon filename
:param data: icon raw image
:param data_base64: icon base 64 image
:return:
'''
# Menu
if menu is not None:
self.Menu = menu
qmenu = QMenu()
qmenu.setTitle(self.Menu[0])
AddTrayMenuItem(qmenu, self.Menu[1], self)
self.TrayIcon.setContextMenu(qmenu)
# Tooltip
if tooltip is not None:
self.TrayIcon.setToolTip(str(tooltip))
# Icon
qicon = None
if filename is not None:
qicon = QIcon(filename)
elif data is not None:
ba = QtCore.QByteArray.fromRawData(data)
pixmap = QtGui.QPixmap()
pixmap.loadFromData(ba)
qicon = QIcon(pixmap)
elif data_base64 is not None:
ba = QtCore.QByteArray.fromBase64(data_base64)
pixmap = QtGui.QPixmap()
pixmap.loadFromData(ba)
qicon = QIcon(pixmap)
if qicon is not None:
self.TrayIcon.setIcon(qicon)
# ------------------------------------------------------------------------- #
@ -5026,6 +5074,13 @@ def start_window_read_timer(window, amount):
return timer
def start_systray_read_timer(tray, amount):
timer = QtCore.QTimer()
timer.timeout.connect(tray.timer_timeout)
timer.start(amount)
return timer
def start_window_autoclose_timer(window, amount):
timer = QtCore.QTimer()
window.autoclose_timer = timer
@ -5164,7 +5219,7 @@ def StartupTK(window):
if not window.FormRemainedOpen:
_my_windows.Decrement()
if window.RootNeedsDestroying:
print('** Destroying window **')
# print('** Destroying window **')
window.QT_QMainWindow.close() # destroy the window
window.RootNeedsDestroying = False
return
@ -5569,17 +5624,17 @@ def EasyPrintClose():
# ======================== Scrolled Text Box =====#
# ===================================================#
def PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None,
size=(None, None)):
size=(None, None), location=(None, None)):
if not args: return
width, height = size
width = width if width else MESSAGE_BOX_LINE_WIDTH
form = Window(args[0], auto_size_text=True, button_color=button_color, auto_close=auto_close,
auto_close_duration=auto_close_duration)
auto_close_duration=auto_close_duration, location=location)
max_line_total, max_line_width, total_lines, height_computed = 0, 0, 0, 0
complete_output = ''
for message in args:
# fancy code to check if string and convert if not is not need. Just always convert to string :-)
# if not isinstance(message, str): message = str(message)
# if not isinstance(message, str): message = str(message) - new
message = str(message)
longest_line_len = max([len(l) for l in message.split('\n')])
width_used = min(longest_line_len, width)
@ -5592,16 +5647,19 @@ def PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto
height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed
if height:
height_computed = height
form.AddRow(Multiline(complete_output, size=(max_line_width, height_computed)))
computed_size = (max_line_width*10, height_computed*16)
form.AddRow(MultilineOutput(complete_output, size=computed_size))
pad = max_line_total - 15 if max_line_total > 15 else 1
# show either an OK or Yes/No depending on paramater
if yes_no:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No())
button, values = form.Read()
form.Close()
return button
else:
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Button('OK', size=(5, 1), button_color=button_color))
button, values = form.Read()
form.Close()
return button

View File

@ -26,7 +26,7 @@
"Qt without the ugly"
## The Alpha Release Version 0.13.0
## The Alpha Release Version 0.18.0
[Announcements of Latest Developments](https://github.com/MikeTheWatchGuy/PySimpleGUI/issues/142)
@ -37,7 +37,7 @@ Welcome to the Alpha Release of PySimpleGUI for Qt!
You can use the exact same code that you are running on the older, tkinter, version of PySimpleGUI.
PySimpleGUIQt uses **PySide2** OR **PyQt5** for access to Qt.
PySimpleGUIQt uses **PySide2** OR **PyQt5** for access to Qt. PyQt5 has been having a number of problems recently however so tread lightly.
## Porting your PySimpleGUI code to PySimpleGUIQt
@ -66,14 +66,14 @@ Fonts should be in the format (font family, size). You can use the older string
On Linux systems you need to run pip3.
pip3 install --upgrade PySimpleGUIQt
### Installing PySide2 or PyQt5 for Python 3
### Installing PySide2 for Python 3
It is recommended that you use PySide2, however, if that cannot be found, then PyQt5 will be attempted. To install either of these:
```pip install PySide2```
or
```pip install PyQt5```
**Nov 26th - There has been a number of problems found using PyQt5 recently. Unclear how if it can be supported after all.**
## Testing your installation
Once you have installed, or copied the .py file to your app folder, you can test the installation using python. At the command prompt start up Python.
@ -91,8 +91,7 @@ Here is the window you should see:
## Prerequisites Python 3
PySide2 or PyQt5
PySide2 or PyQt5 (experimental)
## Using - Python 3
@ -164,60 +163,102 @@ There are a number of new features that are only available in PySimpleGUIQt. Th
## SystemTray
In addition to running normal windows, it's now also possible to have an icon down in the system tray that you can read to get menu events. There is a new SystemTray object that is used much like a Window object. You first get one, then you perform Reads in order to get events. In this case the only events you'll receive are menu selections and timeouts.
This is a PySimpleGUIQt only feature. Don't know of a way to do it using tkinter. It looks likely to work on WxPython however.
In addition to running normal windows, it's now also possible to have an icon down in the system tray that you can read to get menu events. There is a new SystemTray object that is used much like a Window object. You first get one, then you perform Reads in order to get events.
Here is the definition of the SystemTray object.
```python
SystemTray:(title, filename=None, menu=None, data=None, data_base64=None):
SystemTray(menu=None, filename=None, data=None, data_base64=None, tooltip=None):
'''
SystemTray - create an icon in the system tray
:param title: Not currently used. A placeholder / name reminder
:param filename: PNG/ICO/? file that will be used for icon
:param menu:
:param data: In-RAM image to be used for icon
:param data_base64: Base64 data to be used for icon
'''
:param menu: Menu definition
:param filename: filename for icon
:param data: in-ram image for icon
:param data_base64: basee-64 data for icon
:param tooltip: tooltip string '''
```
You'll notice that there are 3 different ways to specify the icon image. The base-64 parameter allows you to define a variable in your .py code that is the encoded image so that you do not need any additional files. Very handy feature.
### System Tray Design Pattern
## System Tray Design Pattern
Here is a design pattern you can use to get a jump-start.
This program will create a system tray icon and perform a blocking Read. If the item "Open" is chosen from the system tray, then a window is shown on the screen.
This program will create a system tray icon and perform a blocking Read. If the item "Open" is chosen from the system tray, then a popup is shown.
```python
import PySimpleGUIQt as sg
menu_def = ['File', ['&Open', '&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]
menu_def = ['BLANK', ['&Open', '---', '&Save', ['1', '2', ['a', 'b']], '&Properties', 'E&xit']]
tray = sg.SystemTray('My Tray', menu=menu_def, filename=r'default_icon.ico')
while True:
menu_item = tray.Read()
if menu_item is not None: print(menu_item)
tray = sg.SystemTray(menu=menu_def, filename=r'default_icon.ico')
while True: # The event loop
menu_item = tray.Read()
print(menu_item)
if menu_item == 'Exit':
break
if menu_item == 'Open':
window = sg.Window('My win').Layout([[sg.Text('My layout')]])
event, values = window.Read()
print(event, values)
elif menu_item == 'Open':
sg.Popup('Menu item chosen', menu_item)
```
The design pattern creates an icon that will display this menu:
![snag-0293](https://user-images.githubusercontent.com/13696193/49057441-8bbfe980-f1cd-11e8-93e7-1aeda9ccd173.jpg)
### Icons
When specifying "icons", you can use 3 different formats.
* `filename`- filename
* `data_base64` - base64 byte string
* '`data` - in-ram bitmap or other "raw" image
You will find 3 parameters used to specify these 3 options on both the initialize statement and on the Update method.
## Menu Definition
```python
menu_def = ['BLANK', ['&Open', '&Save', ['1', '2', ['a', 'b']], '&Properties', 'E&xit']]
```
A menu is defined using a list. A "Menu entry" is a string that specifies:
* text shown
* keyboard shortcut
* key
See section on Menu Keys for more informatoin on using keys with menus.
An entry without a key and keyboard shortcut is a simple string
`'Menu Item'`
If you want to make the "M" be a keyboard shortcut, place an `&` in front of the letter that is the shortcut.
`'&Menu Item'`
You can add "keys" to make menu items unique or as another way of identifying a menu item than the text shown. The key is added to the text portion by placing `::` after the text.
`'Menu Item::key'`
The first entry can be ignored.`'BLANK`' was chosen for this example. It's this way because normally you would specify these menus under some heading on a menu-bar. But here there is no heading so it's filled in with any value you want.
**Separators**
If you want a separator between 2 items, add the entry `'---'` and it will add a separator item at that place in your menu.
## SystemTray Methods
### Read - Read the context menu or check for events
```python
def Read(timeout=None):
def Read(timeout=None)
'''
Reads the context menu
:param timeout: Optional. Any value other than None indicates a non-blocking read
:return: String representing meny item chosen. None if nothing read.
'''
```
The `timeout` parameter specifies how long to wait for an event to take place. If nothing happens within the timeout period, then a "timeout event" is returned. These types of reads make it possible to run asynchronously. To run non-blocked, specify `timeout=0`on the Read call.
Read returns the menu text, complete with key, for the menu item chosen. If you specified `Open::key` as the menu entry, and the user clicked on `Open`, then you will receive the string `Open::key` upon completion of the Read.
#### Read special return values
@ -228,12 +269,20 @@ 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
Hides the icon. Note that no message balloons are shown while an icon is hidden.
```python
def Hide():
def Hide()
```
### Close
Does the same thing as hide
```python
def Close()
```
@ -242,15 +291,26 @@ def Hide():
Shows a previously hidden icon
```python
def UnHide():
def UnHide()
```
### ShowMessage
Shows a balloon above the icon in the system tray area
Shows a balloon above the icon in the system tray area. You can specify your own icon to be shown in the balloon, or you can set `messageicon` to one of the preset values.
This message has a custom icon.
![snag-0286](https://user-images.githubusercontent.com/13696193/49057459-a85c2180-f1cd-11e8-9a66-aa331d7e034c.jpg)
The preset `messageicon` values are:
SYSTEM_TRAY_MESSAGE_ICON_INFORMATION
SYSTEM_TRAY_MESSAGE_ICON_WARNING
SYSTEM_TRAY_MESSAGE_ICON_CRITICAL
SYSTEM_TRAY_MESSAGE_ICON_NOICON
```python
def ShowMessage(title, message, filename=None, data=None, data_base64=None, time=10000):
ShowMessage(title, message, filename=None, data=None, data_base64=None, messageicon=None, time=10000):
'''
Shows a balloon above icon in system tray
:param title: Title shown in balloon
@ -258,11 +318,35 @@ def ShowMessage(title, message, filename=None, data=None, data_base64=None, time
:param filename: Optional icon filename
:param data: Optional in-ram icon
:param data_base64: Optional base64 icon
:param time: How long to display message in milliseconds
:return: self (for call chaining)
'''
:param time: How long to display message in milliseconds :return:
'''
```
Note, on windows it may be necessary to make a registry change to enable message balloons to be seen. To fix this, you must create the DWORD you see in this screenshot.
![snag-0285](https://user-images.githubusercontent.com/13696193/49056144-6381bc00-f1c8-11e8-9f44-199394823369.jpg)
### Update
You can update any of these items within a SystemTray object
* Menu definition
* Icon
* Tooltip
Change them all or just 1.
```python
Update(menu=None, tooltip=None,filename=None, data=None, data_base64=None,)
'''
Updates the menu, tooltip or icon
:param menu: menu defintion
:param tooltip: string representing tooltip
:param filename: icon filename
:param data: icon raw image
:param data_base64: icon base 64 image
:return:
'''
```
## 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.
@ -373,6 +457,18 @@ Menubar now returns values as does the ButtonMenu
Window.Hide and UnHide methods
### 0.18.0 26-Nov-2018
Tooltips for all elements
Completion of all SystemTray features
Read with or without timeout
Specify icons from 3 sources
Show message with custom or preset icons
Update
* Menu
* Tooltip
* Icon
PopupScrolled - new location parameter, fixed bug that wasn't closing window when completed
# Design
## Author