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'
|
||||
|
||||
|
||||
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')
|
|
@ -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)
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue