Release 0.18.0 Qt
This commit is contained in:
parent
c8556d517f
commit
0c651c186c
|
@ -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'
|
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
|
import PySimpleGUIQt as sg
|
||||||
|
|
||||||
# create and then hide the tray icon
|
# create and then hide the tray icon
|
||||||
menu_def = ['My Menu Def', ['&Restore', '&Open', '&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]
|
menu_def = ['My Menu Def', ['&Restore', '&Open','---', '&Message','&Save',['1', '2', ['a','b']], '&Properties', 'E&xit']]
|
||||||
tray = sg.SystemTray('Title', menu=menu_def, data_base64=logo)
|
tray = sg.SystemTray(menu=menu_def, data_base64=logo)
|
||||||
tray.Hide()
|
tray.Hide()
|
||||||
|
|
||||||
# create the window
|
# create the window
|
||||||
|
@ -45,8 +44,7 @@ while True: # the event loop
|
||||||
tray.UnHide()
|
tray.UnHide()
|
||||||
tray_visible = True
|
tray_visible = True
|
||||||
window_visible = False
|
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
|
# do tray icon stuff. Tray events will cause the window.Read call to return with a Timeout event
|
||||||
if tray_visible:
|
if tray_visible:
|
||||||
menu_item = tray.Read()
|
menu_item = tray.Read()
|
||||||
|
@ -67,6 +65,7 @@ while True: # the event loop
|
||||||
window.UnHide()
|
window.UnHide()
|
||||||
else: # some other tray command was received
|
else: # some other tray command was received
|
||||||
print('Menu item %s'%menu_item)
|
print('Menu item %s'%menu_item)
|
||||||
if menu_item == 'Exit':
|
if menu_item == 'Exit' or menu_item == sg.EVENT_SYSTEM_TRAY_MESSAGE_CLICKED:
|
||||||
break
|
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')
|
|
@ -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()
|
|
@ -113,6 +113,7 @@ DEFAULT_DEBUG_WINDOW_SIZE = (800, 400)
|
||||||
DEFAULT_WINDOW_LOCATION = (None, None)
|
DEFAULT_WINDOW_LOCATION = (None, None)
|
||||||
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
|
MAX_SCROLLED_TEXT_BOX_HEIGHT = 50
|
||||||
DEFAULT_TOOLTIP_TIME = 400
|
DEFAULT_TOOLTIP_TIME = 400
|
||||||
|
DEFAULT_TOOLTIP_OFFSET = (20,-20)
|
||||||
#################### COLOR STUFF ####################
|
#################### COLOR STUFF ####################
|
||||||
BLUES = ("#082567", "#0A37A3", "#00345B")
|
BLUES = ("#082567", "#0A37A3", "#00345B")
|
||||||
PURPLES = ("#480656", "#4F2398", "#380474")
|
PURPLES = ("#480656", "#4F2398", "#380474")
|
||||||
|
@ -2684,21 +2685,19 @@ class ErrorElement(Element):
|
||||||
# Tray CLASS #
|
# Tray CLASS #
|
||||||
# ------------------------------------------------------------------------- #
|
# ------------------------------------------------------------------------- #
|
||||||
class SystemTray:
|
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
|
SystemTray - create an icon in the system tray
|
||||||
:param title:
|
:param menu: Menu definition
|
||||||
:param filename:
|
:param filename: filename for icon
|
||||||
:param menu:
|
:param data: in-ram image for icon
|
||||||
:param data:
|
:param data_base64: basee-64 data for icon
|
||||||
:param data_base64:
|
:param tooltip: tooltip string
|
||||||
'''
|
'''
|
||||||
self.Title = title
|
|
||||||
self.Menu = menu
|
self.Menu = menu
|
||||||
self.TrayIcon = None
|
self.TrayIcon = None
|
||||||
self.Shown = False
|
self.Shown = False
|
||||||
self.MenuItemChosen = TIMEOUT_KEY
|
self.MenuItemChosen = TIMEOUT_KEY
|
||||||
self.Tooltip = tooltip
|
|
||||||
|
|
||||||
global _my_windows
|
global _my_windows
|
||||||
|
|
||||||
|
@ -2725,16 +2724,17 @@ class SystemTray:
|
||||||
return
|
return
|
||||||
self.TrayIcon = QSystemTrayIcon(qicon)
|
self.TrayIcon = QSystemTrayIcon(qicon)
|
||||||
|
|
||||||
qmenu = QMenu()
|
if self.Menu is not None:
|
||||||
qmenu.setTitle(self.Menu[0])
|
qmenu = QMenu()
|
||||||
AddTrayMenuItem(qmenu, self.Menu[1], self)
|
qmenu.setTitle(self.Menu[0])
|
||||||
|
AddTrayMenuItem(qmenu, self.Menu[1], self)
|
||||||
|
self.TrayIcon.setContextMenu(qmenu)
|
||||||
|
|
||||||
if self.Tooltip is not None:
|
if tooltip is not None:
|
||||||
self.TrayIcon.setToolTip(str(self.Tooltip))
|
self.TrayIcon.setToolTip(str(tooltip))
|
||||||
|
|
||||||
self.TrayIcon.messageClicked.connect(self.messageClicked)
|
self.TrayIcon.messageClicked.connect(self.messageClicked)
|
||||||
self.TrayIcon.activated.connect(self.doubleClicked)
|
self.TrayIcon.activated.connect(self.doubleClicked)
|
||||||
self.TrayIcon.setContextMenu(qmenu)
|
|
||||||
|
|
||||||
self.TrayIcon.show()
|
self.TrayIcon.show()
|
||||||
|
|
||||||
|
@ -2766,19 +2766,22 @@ class SystemTray:
|
||||||
if not self.Shown:
|
if not self.Shown:
|
||||||
self.Shown = True
|
self.Shown = True
|
||||||
self.TrayIcon.show()
|
self.TrayIcon.show()
|
||||||
if timeout is None:
|
if timeout is None:
|
||||||
self.App.exec_()
|
self.App.exec_()
|
||||||
else:
|
elif timeout == 0:
|
||||||
self.App.processEvents()
|
self.App.processEvents()
|
||||||
else:
|
else:
|
||||||
if timeout is None:
|
self.timer = start_systray_read_timer(self, timeout)
|
||||||
self.App.exec_()
|
self.App.exec_()
|
||||||
else:
|
if self.timer:
|
||||||
self.App.processEvents()
|
stop_timer(self.timer)
|
||||||
|
|
||||||
item = self.MenuItemChosen
|
item = self.MenuItemChosen
|
||||||
self.MenuItemChosen = TIMEOUT_KEY
|
self.MenuItemChosen = TIMEOUT_KEY
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
def timer_timeout(self):
|
||||||
|
self.App.exit() # kick the users out of the mainloop
|
||||||
|
|
||||||
def Hide(self):
|
def Hide(self):
|
||||||
self.TrayIcon.hide()
|
self.TrayIcon.hide()
|
||||||
|
@ -2823,7 +2826,52 @@ class SystemTray:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def Close(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
|
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):
|
def start_window_autoclose_timer(window, amount):
|
||||||
timer = QtCore.QTimer()
|
timer = QtCore.QTimer()
|
||||||
window.autoclose_timer = timer
|
window.autoclose_timer = timer
|
||||||
|
@ -5164,7 +5219,7 @@ def StartupTK(window):
|
||||||
if not window.FormRemainedOpen:
|
if not window.FormRemainedOpen:
|
||||||
_my_windows.Decrement()
|
_my_windows.Decrement()
|
||||||
if window.RootNeedsDestroying:
|
if window.RootNeedsDestroying:
|
||||||
print('** Destroying window **')
|
# print('** Destroying window **')
|
||||||
window.QT_QMainWindow.close() # destroy the window
|
window.QT_QMainWindow.close() # destroy the window
|
||||||
window.RootNeedsDestroying = False
|
window.RootNeedsDestroying = False
|
||||||
return
|
return
|
||||||
|
@ -5569,17 +5624,17 @@ def EasyPrintClose():
|
||||||
# ======================== Scrolled Text Box =====#
|
# ======================== Scrolled Text Box =====#
|
||||||
# ===================================================#
|
# ===================================================#
|
||||||
def PopupScrolled(*args, button_color=None, yes_no=False, auto_close=False, auto_close_duration=None,
|
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
|
if not args: return
|
||||||
width, height = size
|
width, height = size
|
||||||
width = width if width else MESSAGE_BOX_LINE_WIDTH
|
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,
|
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
|
max_line_total, max_line_width, total_lines, height_computed = 0, 0, 0, 0
|
||||||
complete_output = ''
|
complete_output = ''
|
||||||
for message in args:
|
for message in args:
|
||||||
# fancy code to check if string and convert if not is not need. Just always convert to string :-)
|
# 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)
|
message = str(message)
|
||||||
longest_line_len = max([len(l) for l in message.split('\n')])
|
longest_line_len = max([len(l) for l in message.split('\n')])
|
||||||
width_used = min(longest_line_len, width)
|
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
|
height_computed = MAX_SCROLLED_TEXT_BOX_HEIGHT if height_computed > MAX_SCROLLED_TEXT_BOX_HEIGHT else height_computed
|
||||||
if height:
|
if height:
|
||||||
height_computed = 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
|
pad = max_line_total - 15 if max_line_total > 15 else 1
|
||||||
# show either an OK or Yes/No depending on paramater
|
# show either an OK or Yes/No depending on paramater
|
||||||
if yes_no:
|
if yes_no:
|
||||||
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No())
|
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Yes(), No())
|
||||||
button, values = form.Read()
|
button, values = form.Read()
|
||||||
|
form.Close()
|
||||||
return button
|
return button
|
||||||
else:
|
else:
|
||||||
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Button('OK', size=(5, 1), button_color=button_color))
|
form.AddRow(Text('', size=(pad, 1), auto_size_text=False), Button('OK', size=(5, 1), button_color=button_color))
|
||||||
button, values = form.Read()
|
button, values = form.Read()
|
||||||
|
form.Close()
|
||||||
return button
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
"Qt without the ugly"
|
"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)
|
[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.
|
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
|
## Porting your PySimpleGUI code to PySimpleGUIQt
|
||||||
|
|
||||||
|
@ -66,15 +66,15 @@ Fonts should be in the format (font family, size). You can use the older string
|
||||||
On Linux systems you need to run pip3.
|
On Linux systems you need to run pip3.
|
||||||
|
|
||||||
pip3 install --upgrade PySimpleGUIQt
|
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:
|
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```
|
```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
|
## 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.
|
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,10 +91,9 @@ Here is the window you should see:
|
||||||
|
|
||||||
|
|
||||||
## Prerequisites Python 3
|
## Prerequisites Python 3
|
||||||
PySide2 or PyQt5
|
PySide2 or PyQt5 (experimental)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Using - Python 3
|
## Using - Python 3
|
||||||
To use in your code, simply import....
|
To use in your code, simply import....
|
||||||
`import PySimpleGUIQt as sg`
|
`import PySimpleGUIQt as sg`
|
||||||
|
@ -164,60 +163,102 @@ There are a number of new features that are only available in PySimpleGUIQt. Th
|
||||||
|
|
||||||
## SystemTray
|
## 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.
|
Here is the definition of the SystemTray object.
|
||||||
|
|
||||||
```python
|
```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
|
SystemTray - create an icon in the system tray
|
||||||
:param title: Not currently used. A placeholder / name reminder
|
:param menu: Menu definition
|
||||||
:param filename: PNG/ICO/? file that will be used for icon
|
:param filename: filename for icon
|
||||||
:param menu:
|
:param data: in-ram image for icon
|
||||||
:param data: In-RAM image to be used for icon
|
:param data_base64: basee-64 data for icon
|
||||||
:param data_base64: Base64 data to be used 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.
|
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.
|
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
|
```python
|
||||||
import PySimpleGUIQt as sg
|
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')
|
tray = sg.SystemTray(menu=menu_def, filename=r'default_icon.ico')
|
||||||
|
|
||||||
while True:
|
|
||||||
menu_item = tray.Read()
|
|
||||||
if menu_item is not None: print(menu_item)
|
|
||||||
|
|
||||||
|
while True: # The event loop
|
||||||
|
menu_item = tray.Read()
|
||||||
|
print(menu_item)
|
||||||
if menu_item == 'Exit':
|
if menu_item == 'Exit':
|
||||||
break
|
break
|
||||||
if menu_item == 'Open':
|
elif menu_item == 'Open':
|
||||||
window = sg.Window('My win').Layout([[sg.Text('My layout')]])
|
sg.Popup('Menu item chosen', menu_item)
|
||||||
event, values = window.Read()
|
|
||||||
print(event, values)
|
|
||||||
```
|
```
|
||||||
|
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
|
## SystemTray Methods
|
||||||
|
|
||||||
### Read - Read the context menu or check for events
|
### Read - Read the context menu or check for events
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def Read(timeout=None):
|
def Read(timeout=None)
|
||||||
'''
|
'''
|
||||||
Reads the context menu
|
Reads the context menu
|
||||||
:param timeout: Optional. Any value other than None indicates a non-blocking read
|
:param timeout: Optional. Any value other than None indicates a non-blocking read
|
||||||
:return: String representing meny item chosen. None if nothing 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
|
#### 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
|
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
|
TIMEOUT_KEY is returned if no events are available if the timeout value is set in the Read call
|
||||||
|
|
||||||
|
|
||||||
### Hide
|
### Hide
|
||||||
|
|
||||||
Hides the icon
|
Hides the icon. Note that no message balloons are shown while an icon is hidden.
|
||||||
|
|
||||||
```python
|
```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
|
Shows a previously hidden icon
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def UnHide():
|
def UnHide()
|
||||||
```
|
```
|
||||||
|
|
||||||
### ShowMessage
|
### 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
|
```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
|
Shows a balloon above icon in system tray
|
||||||
:param title: Title shown in balloon
|
: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 filename: Optional icon filename
|
||||||
:param data: Optional in-ram icon
|
:param data: Optional in-ram icon
|
||||||
:param data_base64: Optional base64 icon
|
:param data_base64: Optional base64 icon
|
||||||
:param time: How long to display message in milliseconds
|
:param time: How long to display message in milliseconds :return:
|
||||||
:return: self (for call chaining)
|
'''
|
||||||
'''
|
|
||||||
```
|
```
|
||||||
|
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
|
## 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.
|
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
|
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
|
# Design
|
||||||
## Author
|
## Author
|
||||||
|
|
Loading…
Reference in New Issue