Compare commits

..

No commits in common. "master" and "4.58.0" have entirely different histories.

376 changed files with 9891 additions and 21378 deletions

View file

@ -60,13 +60,11 @@ Anything else you think would be helpful?
These items may solve your problem. Please check those you've done by changing - [ ] to - [X]
- [ ] Searched main docs for your problem www.PySimpleGUI.org
- [ ] Looked for Demo Programs that are similar to your goal. It is recommend you use the Demo Browser! Demos.PySimpleGUI.org
- [ ] None of your GUI code was generated by an AI algorithm like GPT
- [ ] Looked for Demo Programs that are similar to your goal Demos.PySimpleGUI.org
- [ ] If not tkinter - looked for Demo Programs for specific port
- [ ] For non tkinter - Looked at readme for your specific port if not PySimpleGUI (Qt, WX, Remi)
- [ ] Run your program outside of your debugger (from a command line)
- [ ] Searched through Issues (open and closed) to see if already reported Issues.PySimpleGUI.org
- [ ] Have upgraded to the latest release of PySimpleGUI on PyPI (lastest official version)
- [ ] Tried using the PySimpleGUI.py file on GitHub. Your problem may have already been fixed but not released
#### Detailed Description

View file

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1,000 B

After

Width:  |  Height:  |  Size: 1,000 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

@ -1,39 +1,26 @@
## Contributing to PySimpleGUI
Hi there! Mike here....thank you for taking time to read this document.
### Open Source License, but Private Development
PySimpleGUI is different than most projects on GitHub. It is licensed using the "Open Source License" LGPL3. However, the coding and development of the project is not structured in the same way most open source projects are structured.
PySimpleGUI is different than most projects on GitHub. It is licensed using the "Open Source License" LGPL3. However, the coding and development of the project is not "open source".
This project/account does not accept user submitted code nor documentation.
This project does not accept user submitted code.
### You Can Still Contribute
#### Write Applications, Use PySimpleGUI, Write Tutorials, Teach Others
#### Write Applications, Use PySimpleGUI, Make Repos, Post Screenshots, Write Tutorials, Teach Others
These are a few of the ways you can directly contribute to PySimpleGUI. Using the package to make cool stuff and helping others learn how to use it to make cool stuff and a big help to PySimpleGUI. **Everyone** learns from seeing other people's implementations. It's through user's creating applications that new problems and needs are discovered. These have had a profound and positive impact on the project in the past.
These are a few of the ways you can directly contribute to PySimpleGUI. Using the package to make cool stuff and helping others learn how to use it to make cool stuff is a big help to PySimpleGUI. **Everyone** learns from seeing other people's implementations. It's through user's creating applications that new problems and needs are discovered. These have had a profound and positive impact on the project in the past.
#### Pull Requests
#### Make Suggestions
There are 100's of open issues in the main PySimpleGUI GitHub account that are actively worked, daily. There are 1,000s that have been completed. The evolution of PySimpleGUI over the years has been a combination of my vision for the product and ideas from users. So many people have helped make PySimpleGUI better.
### Pull Requests
Pull requests are *not being accepted* for the project. This includes sending code changes via other means than "pull requests". Plainly put, code you send will not be used.
I don't mean to be ugly. This isn't personal. Heck, I don't know "you",the reader personally. It's not about ego. It's complicated. The result is that it allows me to dedicate my life to this project. It's what's required, for whatever reason, for me to do this. That's the best explanation I have. I love and respect the users of this work.
Pull requests are *not being accepted* for the project. This includes sending code changes via other means than "pull requests". Plainly put, core code you send will not be used.
### Bug Fixes
#### Bug Fixes
If you file an Issue for a bug, have located the bug, and found a fix in 10 lines of code or less.... and you wish to share your fix with the community, then feel free to include it with the filed Issue. If it's longer than 10 lines and wish to discuss it, then send an email to help@PySimpleGUI.org.
## Thank You
This project comes from a well-meaning, love of computing, and helping others place. It's not about "me", it's about ***you***.
The support from the user community has been ***amazing***. Your passion for creating PySimpleGUI applications is infectious. Every "thank you" is noticed and appreciated! Your passion for wanting to see PySimpleGUI improve is neither ignored nor unappreciated. At a time when the Internet can feel toxic, there's been expressions of appreciation, gratitude, and encouragement that's unbelievable. I'm touched on a very frequent basis and am filled with gratitude myself as a result.
It's understood that this way of development of a Python package is unorthodox. You may find it frustrating and slow, but hope you can respect the decision for it to operate in this manner and be supportive.
The support from the user community has been amazing. Your passion for creating PySimpleGUI applications is infectious. Every "thank you" is noticed and appreciated! Your passion for wanting to see PySimpleGUI improve is neither ignored nor unappreciated.
It's understood that this way of development of a Python package is unorthodox. You may find it frustrating and slow, but hope you can respect the decision for it to operate in this manner and be supportive.

BIN
Chess/ChessPiecesArray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

View file

@ -0,0 +1,233 @@
import PySimpleGUI as sg
import os
import sys
import chess
import chess.pgn
import copy
import chess.uci
CHESS_PATH = '.' # path to the chess pieces
BLANK = 0 # piece names
PAWNB = 1
KNIGHTB = 2
BISHOPB = 3
ROOKB = 4
KINGB = 5
QUEENB = 6
PAWNW = 7
KNIGHTW = 8
BISHOPW = 9
ROOKW = 10
KINGW = 11
QUEENW = 12
initial_board = [[ROOKB, KNIGHTB, BISHOPB, QUEENB, KINGB, BISHOPB, KNIGHTB, ROOKB],
[PAWNB, ] * 8,
[BLANK, ] * 8,
[BLANK, ] * 8,
[BLANK, ] * 8,
[BLANK, ] * 8,
[PAWNW, ] * 8,
[ROOKW, KNIGHTW, BISHOPW, QUEENW, KINGW, BISHOPW, KNIGHTW, ROOKW]]
blank = os.path.join(CHESS_PATH, 'blank.png')
bishopB = os.path.join(CHESS_PATH, 'nbishopb.png')
bishopW = os.path.join(CHESS_PATH, 'nbishopw.png')
pawnB = os.path.join(CHESS_PATH, 'npawnb.png')
pawnW = os.path.join(CHESS_PATH, 'npawnw.png')
knightB = os.path.join(CHESS_PATH, 'nknightb.png')
knightW = os.path.join(CHESS_PATH, 'nknightw.png')
rookB = os.path.join(CHESS_PATH, 'nrookb.png')
rookW = os.path.join(CHESS_PATH, 'nrookw.png')
queenB = os.path.join(CHESS_PATH, 'nqueenb.png')
queenW = os.path.join(CHESS_PATH, 'nqueenw.png')
kingB = os.path.join(CHESS_PATH, 'nkingb.png')
kingW = os.path.join(CHESS_PATH, 'nkingw.png')
images = {BISHOPB: bishopB, BISHOPW: bishopW, PAWNB: pawnB, PAWNW: pawnW, KNIGHTB: knightB, KNIGHTW: knightW,
ROOKB: rookB, ROOKW: rookW, KINGB: kingB, KINGW: kingW, QUEENB: queenB, QUEENW: queenW, BLANK: blank}
def open_pgn_file(filename):
pgn = open(filename)
first_game = chess.pgn.read_game(pgn)
moves = [move for move in first_game.main_line()]
return moves
def render_square(image, key, location):
if (location[0] + location[1]) % 2:
color = '#B58863'
else:
color = '#F0D9B5'
return sg.RButton('', image_filename=image, size=(1, 1), button_color=('white', color), pad=(0, 0), key=key)
def redraw_board(window, board):
for i in range(8):
for j in range(8):
color = '#B58863' if (i + j) % 2 else '#F0D9B5'
piece_image = images[board[i][j]]
elem = window.FindElement(key=(i, j))
elem.Update(button_color=('white', color),
image_filename=piece_image, )
def PlayGame():
menu_def = [['&File', ['&Open PGN File', 'E&xit']],
['&Help', '&About...'], ]
# sg.SetOptions(margins=(0,0))
sg.ChangeLookAndFeel('GreenTan')
# create initial board setup
psg_board = copy.deepcopy(initial_board)
# the main board display layout
board_layout = [[sg.T(' ')] + [sg.T('{}'.format(a), pad=((23, 27), 0), font='Any 13') for a in 'abcdefgh']]
# loop though board and create buttons with images
for i in range(8):
row = [sg.T(str(8 - i) + ' ', font='Any 13')]
for j in range(8):
piece_image = images[psg_board[i][j]]
row.append(render_square(piece_image, key=(i, j), location=(i, j)))
row.append(sg.T(str(8 - i) + ' ', font='Any 13'))
board_layout.append(row)
# add the labels across bottom of board
board_layout.append([sg.T(' ')] + [sg.T('{}'.format(a), pad=((23, 27), 0), font='Any 13') for a in 'abcdefgh'])
# setup the controls on the right side of screen
openings = (
'Any', 'Defense', 'Attack', 'Trap', 'Gambit', 'Counter', 'Sicillian', 'English', 'French', 'Queen\'s openings',
'King\'s Openings', 'Indian Openings')
board_controls = [[sg.RButton('New Game', key='New Game'), sg.RButton('Draw')],
[sg.RButton('Resign Game'), sg.RButton('Set FEN')],
[sg.RButton('Player Odds'), sg.RButton('Training')],
[sg.Drop(openings), sg.Text('Opening/Style')],
[sg.CBox('Play As White', key='_white_')],
[sg.Drop([2, 3, 4, 5, 6, 7, 8, 9, 10], size=(3, 1), key='_level_'), sg.Text('Difficulty Level')],
[sg.Text('Move List')],
[sg.Multiline([], do_not_clear=True, autoscroll=True, size=(15, 10), key='_movelist_')],
]
# layouts for the tabs
controls_layout = [[sg.Text('Performance Parameters', font='_ 20')],
[sg.T('Put stuff like AI engine tuning parms on this tab')]]
statistics_layout = [[sg.Text('Statistics', font=('_ 20'))],
[sg.T('Game statistics go here?')]]
board_tab = [[sg.Column(board_layout)]]
# the main window layout
layout = [[sg.Menu(menu_def, tearoff=False)],
[sg.TabGroup([[sg.Tab('Board', board_tab),
sg.Tab('Controls', controls_layout),
sg.Tab('Statistics', statistics_layout)]], title_color='red'),
sg.Column(board_controls)],
[sg.Text('Click anywhere on board for next move', font='_ 14')]]
window = sg.Window('Chess',
default_button_element_size=(12, 1),
auto_size_buttons=False,
icon='kingb.ico').Layout(layout)
filename = sg.PopupGetFile('\n'.join(('To begin, set location of AI EXE file',
'If you have not done so already, download the engine',
'Download the StockFish Chess engine at: https://stockfishchess.org/download/')),
file_types=(('Chess AI Engine EXE File', '*.exe'),))
if filename is None:
sys.exit()
engine = chess.uci.popen_engine(filename)
engine.uci()
info_handler = chess.uci.InfoHandler()
engine.info_handlers.append(info_handler)
board = chess.Board()
move_count = 1
move_state = move_from = move_to = 0
# ---===--- Loop taking in user input --- #
while not board.is_game_over():
if board.turn == chess.WHITE:
engine.position(board)
# human_player(board)
move_state = 0
while True:
button, value = window.Read()
if button in (None, 'Exit'):
exit()
if button == 'New Game':
sg.Popup('You have to restart the program to start a new game... sorry....')
break
psg_board = copy.deepcopy(initial_board)
redraw_board(window, psg_board)
move_state = 0
break
level = value['_level_']
if type(button) is tuple:
if move_state == 0:
move_from = button
row, col = move_from
piece = psg_board[row][col] # get the move-from piece
button_square = window.FindElement(key=(row, col))
button_square.Update(button_color=('white', 'red'))
move_state = 1
elif move_state == 1:
move_to = button
row, col = move_to
if move_to == move_from: # cancelled move
color = '#B58863' if (row + col) % 2 else '#F0D9B5'
button_square.Update(button_color=('white', color))
move_state = 0
continue
picked_move = '{}{}{}{}'.format('abcdefgh'[move_from[1]], 8 - move_from[0],
'abcdefgh'[move_to[1]], 8 - move_to[0])
if picked_move in [str(move) for move in board.legal_moves]:
board.push(chess.Move.from_uci(picked_move))
else:
print('Illegal move')
move_state = 0
color = '#B58863' if (move_from[0] + move_from[1]) % 2 else '#F0D9B5'
button_square.Update(button_color=('white', color))
continue
psg_board[move_from[0]][move_from[1]] = BLANK # place blank where piece was
psg_board[row][col] = piece # place piece in the move-to square
redraw_board(window, psg_board)
move_count += 1
window.FindElement('_movelist_').Update(picked_move + '\n', append=True)
break
else:
engine.position(board)
best_move = engine.go(searchmoves=board.legal_moves, depth=level, movetime=(level * 100)).bestmove
move_str = str(best_move)
from_col = ord(move_str[0]) - ord('a')
from_row = 8 - int(move_str[1])
to_col = ord(move_str[2]) - ord('a')
to_row = 8 - int(move_str[3])
window.FindElement('_movelist_').Update(move_str + '\n', append=True)
piece = psg_board[from_row][from_col]
psg_board[from_row][from_col] = BLANK
psg_board[to_row][to_col] = piece
redraw_board(window, psg_board)
board.push(best_move)
move_count += 1
sg.Popup('Game over!', 'Thank you for playing')
# Download the StockFish Chess engine at: https://stockfishchess.org/download/
# engine = chess.uci.popen_engine(r'E:\DownloadsE\stockfish-9-win\Windows\stockfish_9_x64.exe')
# engine.uci()
# info_handler = chess.uci.InfoHandler()
# engine.info_handlers.append(info_handler)
# level = 2
PlayGame()

160
Chess/Demo_Chess_Board.py Normal file
View file

@ -0,0 +1,160 @@
import PySimpleGUI as sg
import os
import chess
import chess.pgn
import copy
import time
button_names = ('close', 'cookbook', 'cpu', 'github', 'pysimplegui', 'run', 'storage', 'timer')
CHESS_PATH = '.' # path to the chess pieces
BLANK = 0 # piece names
PAWNB = 1
KNIGHTB = 2
BISHOPB = 3
ROOKB = 4
KINGB = 5
QUEENB = 6
PAWNW = 7
KNIGHTW = 8
BISHOPW = 9
ROOKW = 10
KINGW = 11
QUEENW = 12
initial_board = [[ROOKB, KNIGHTB, BISHOPB, KINGB, QUEENB, BISHOPB, KNIGHTB, ROOKB ],
[PAWNB,]*8,
[BLANK,]*8,
[BLANK,]*8,
[BLANK,]*8,
[BLANK,]*8,
[PAWNW,]*8,
[ROOKW, KNIGHTW, BISHOPW, KINGW, QUEENW, BISHOPW, KNIGHTW, ROOKW]]
blank = os.path.join(CHESS_PATH, 'blank.png')
bishopB = os.path.join(CHESS_PATH, 'nbishopb.png')
bishopW = os.path.join(CHESS_PATH, 'nbishopw.png')
pawnB = os.path.join(CHESS_PATH, 'npawnb.png')
pawnW = os.path.join(CHESS_PATH, 'npawnw.png')
knightB = os.path.join(CHESS_PATH, 'nknightb.png')
knightW = os.path.join(CHESS_PATH, 'nknightw.png')
rookB = os.path.join(CHESS_PATH, 'nrookb.png')
rookW = os.path.join(CHESS_PATH, 'nrookw.png')
queenB = os.path.join(CHESS_PATH, 'nqueenB.png')
queenW = os.path.join(CHESS_PATH, 'nqueenW.png')
kingB = os.path.join(CHESS_PATH, 'nkingb.png')
kingW = os.path.join(CHESS_PATH, 'nkingw.png')
images = {BISHOPB: bishopB, BISHOPW: bishopW, PAWNB: pawnB, PAWNW: pawnW, KNIGHTB: knightB, KNIGHTW: knightW,
ROOKB: rookB, ROOKW: rookW, KINGB: kingB, KINGW: kingW, QUEENB: queenB, QUEENW: queenW, BLANK: blank}
def open_pgn_file(filename):
pgn = open(filename)
first_game = chess.pgn.read_game(pgn)
moves = [move for move in first_game.main_line()]
return moves
def render_square(image, key, location):
if (location[0] + location[1]) % 2:
color = '#B58863'
else:
color = '#F0D9B5'
return sg.RButton('', image_filename=image, size=(1, 1), button_color=('white', color), pad=(0, 0), key=key)
def redraw_board(window, board):
for i in range(8):
for j in range(8):
color = '#B58863' if (i+j) % 2 else '#F0D9B5'
piece_image = images[board[i][j]]
elem = window.FindElement(key=(i,j))
elem.Update(button_color = ('white', color),
image_filename=piece_image,)
def PlayGame():
menu_def = [['&File', ['&Open PGN File', 'E&xit' ]],
['&Help', '&About...'],]
# sg.SetOptions(margins=(0,0))
sg.ChangeLookAndFeel('GreenTan')
# create initial board setup
board = copy.deepcopy(initial_board)
# the main board display layout
board_layout = [[sg.T(' ')] + [sg.T('{}'.format(a), pad=((23,27),0), font='Any 13') for a in 'abcdefgh']]
# loop though board and create buttons with images
for i in range(8):
row = [sg.T(str(8-i)+' ', font='Any 13')]
for j in range(8):
piece_image = images[board[i][j]]
row.append(render_square(piece_image, key=(i,j), location=(i,j)))
row.append(sg.T(str(8-i)+' ', font='Any 13'))
board_layout.append(row)
# add the labels across bottom of board
board_layout.append([sg.T(' ')] + [sg.T('{}'.format(a), pad=((23,27),0), font='Any 13') for a in 'abcdefgh'])
# setup the controls on the right side of screen
openings = ('Any', 'Defense', 'Attack', 'Trap', 'Gambit','Counter', 'Sicillian', 'English','French', 'Queen\'s openings', 'King\'s Openings','Indian Openings')
board_controls = [[sg.RButton('New Game', key='Open PGN File'), sg.RButton('Draw')],
[sg.RButton('Resign Game'), sg.RButton('Set FEN')],
[sg.RButton('Player Odds'),sg.RButton('Training') ],
[sg.Drop(openings),sg.Text('Opening/Style')],
[sg.CBox('Play a White', key='_white_')],
[sg.Text('Move List')],
[sg.Multiline([], do_not_clear=True, autoscroll=True, size=(15,10),key='_movelist_')],]
# layouts for the tabs
controls_layout = [[sg.Text('Performance Parameters', font='_ 20')],
[sg.T('Put stuff like AI engine tuning parms on this tab')]]
statistics_layout = [[sg.Text('Statistics', font=('_ 20'))],
[sg.T('Game statistics go here?')]]
board_tab = [[sg.Column(board_layout)]]
# the main window layout
layout = [[sg.Menu(menu_def, tearoff=False)],
[sg.TabGroup([[sg.Tab('Board',board_tab),
sg.Tab('Controls', controls_layout),
sg.Tab('Statistics', statistics_layout)]], title_color='red'),
sg.Column(board_controls)],
[sg.Text('Click anywhere on board for next move', font='_ 14')]]
window = sg.Window('Chess', default_button_element_size=(12,1), auto_size_buttons=False, icon='kingb.ico').Layout(layout)
# ---===--- Loop taking in user input --- #
i = 0
moves = None
while True:
button, value = window.Read()
if button in (None, 'Exit'):
break
if button == 'Open PGN File':
filename = sg.PopupGetFile('', no_window=True)
if filename is not None:
moves = open_pgn_file(filename)
i = 0
board = copy.deepcopy(initial_board)
window.FindElement('_movelist_').Update(value='')
if button == 'About...':
sg.Popup('Powerd by Engine Kibitz Chess Engine')
if type(button) is tuple and moves is not None and i < len(moves):
move = moves[i] # get the current move
window.FindElement('_movelist_').Update(value='{} {}\n'.format(i+1, str(move)), append=True)
move_from = move.from_square # parse the move-from and move-to squares
move_to = move.to_square
row, col = move_from // 8, move_from % 8
piece = board[row][col] # get the move-from piece
button = window.FindElement(key=(row,col))
for x in range(3):
button.Update(button_color = ('white' , 'red' if x % 2 else 'white'))
window.Refresh()
time.sleep(.05)
board[row][col] = BLANK # place blank where piece was
row, col = move_to // 8, move_to % 8 # compute move-to square
board[row][col] = piece # place piece in the move-to square
redraw_board(window, board)
i += 1
PlayGame()

BIN
Chess/bishopb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
Chess/bishopw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
Chess/blank.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

36
Chess/game.pgn Normal file
View file

@ -0,0 +1,36 @@
[Event "Wch U12"]
[Site "Duisburg"]
[Date "1992.??.??"]
[Round "1"]
[White "Malakhov, Vladimir"]
[Black "Ab Rahman, M."]
[Result "1-0"]
[WhiteElo ""]
[BlackElo ""]
[ECO "A05"]
1.Nf3 Nf6 2.b3 g6 3.Bb2 Bg7 4.g3 d6 5.Bg2 O-O 6.O-O c6 7.d3 e5 8.c4 Ne8 9.Nbd2 f5
10.Qc2 Na6 11.c5 Nxc5 12.Nxe5 Qe7 13.d4 Na6 14.Qc4+ Kh8 15.Nef3 Be6 16.Qc3 f4
17.gxf4 Rxf4 18.Qe3 Rf8 19.Ng5 Nec7 20.Nc4 Rae8 21.Nxe6 Qxe6 22.Qxe6 Rxe6
23.e3 d5 24.Ne5 g5 25.Ba3 Rff6 26.Bh3 Re8 27.Bd7 Rd8 28.Be7 Rxd7 29.Bxf6 1-0
[Event "Wch U12"]
[Site "Duisburg"]
[Date "1992.??.??"]
[Round "2"]
[White "Malakhov, Vladimir"]
[Black "Berescu, Alin"]
[Result "1-0"]
[WhiteElo ""]
[BlackElo ""]
[ECO "D05"]
1.d4 Nf6 2.Nd2 d5 3.Ngf3 e6 4.e3 c5 5.c3 Nbd7 6.Bd3 Bd6 7.O-O O-O 8.Re1 b6
9.e4 dxe4 10.Nxe4 Be7 11.Ne5 Bb7 12.Ng5 g6 13.Qe2 Nxe5 14.dxe5 Nh5 15.Ne4 Qd5
16.f4 Rfd8 17.Bc2 Qc6 18.Be3 Rd7 19.Rad1 Rad8 20.Rxd7 Rxd7 21.Nd2 Ng7 22.Be4 Qc8
23.g4 Qd8 24.Bxb7 Rxb7 25.Ne4 Rd7 26.c4 h5 27.h3 h4 28.Kh2 Ne8 29.f5 Qc7
30.Bf4 Rd4 31.Qf2 Rxc4 32.f6 Qb7 33.Ng5 Bf8 34.b3 Rc3 35.Qd2 Rf3 36.Nxf3 Qxf3
37.Qe3 Qd5 38.Qe4 Qd7 39.Qf3 Nc7 40.Rd1 Nd5 41.Bg5 Qc7 42.Re1 b5 43.Qd1 c4
44.Qc1 Bb4 45.Bd2 Bxd2 46.Qxd2 Nxf6 47.bxc4 bxc4 48.Qd6 Qa5 49.Rf1 Nd5 50.Qd7 Qd2+
51.Kh1 f5 52.exf6 1-0

BIN
Chess/kingb.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
Chess/kingb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
Chess/kingw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/knightb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
Chess/knightw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
Chess/nbishopb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
Chess/nbishopw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/nkingb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/nkingw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
Chess/nknightb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/nknightw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
Chess/npawnb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/npawnw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/nqueenb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
Chess/nqueenw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
Chess/nrookb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
Chess/nrookw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
Chess/pawnb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

BIN
Chess/pawnw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
Chess/queenb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
Chess/queenw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

16
Chess/readme.md Normal file
View file

@ -0,0 +1,16 @@
# PySimpleGUI-Chess A Chess Game Playback Program
![image](https://user-images.githubusercontent.com/46163555/64135781-4c58a600-cdba-11e9-968d-60ddfb4c8952.png)
## Introduction
This is the start of a front-end GUI for an AI engine that plays chess. It simply reads moves the a PGN file and steps through it showing each of the moves on the board.
To play against the AI run the program
Demo_Chess_AGAINST_AI.py
Locate where the pacakge was installed and run the programs from that folder. You need to run from the installed folder so that the images of the chess pieces are located.
## Home Page (GitHub)
[www.PySimpleGUI.com](www.PySimpleGUI.com)

2
Chess/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
PySimpleGUI==3.9.1
python-chess==0.23.9

BIN
Chess/rookb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

BIN
Chess/rookw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

View file

@ -5,7 +5,7 @@ import warnings
import PySimpleGUI as sg
__version__ = '1.12.2'
__version__ = '1.7.0'
"""
PySimpleGUI Demo Program Browser
@ -33,15 +33,9 @@ __version__ = '1.12.2'
Keeps a "history" of the previously chosen folders to easy switching between projects
Versions:
1.8.0 - Addition of option to show ALL file types, not just Python files
1.12.0 - Fix for problem with spaces in filename and using an editor specified in the demo program settings
1.12.2 - Better error handling for no editor configured
Copyright 2021, 2022 PySimpleGUI.org
"""
python_only = True
def get_file_list_dict():
"""
Returns dictionary of files
@ -56,7 +50,7 @@ def get_file_list_dict():
demo_files_dict = {}
for dirname, dirnames, filenames in os.walk(demo_path):
for filename in filenames:
if python_only is not True or filename.endswith('.py') or filename.endswith('.pyw'):
if filename.endswith('.py') or filename.endswith('.pyw'):
fname_full = os.path.join(dirname, filename)
if filename not in demo_files_dict.keys():
demo_files_dict[filename] = fname_full
@ -462,7 +456,7 @@ def make_window():
left_col = sg.Column([
[sg.Listbox(values=get_file_list(), select_mode=sg.SELECT_MODE_EXTENDED, size=(50,20), bind_return_key=True, key='-DEMO LIST-', expand_x=True, expand_y=True)],
[sg.Listbox(values=get_file_list(), select_mode=sg.SELECT_MODE_EXTENDED, size=(50,20), bind_return_key=True, key='-DEMO LIST-')],
[sg.Text('Filter (F1):', tooltip=filter_tooltip), sg.Input(size=(25, 1), focus=True, enable_events=True, key='-FILTER-', tooltip=filter_tooltip),
sg.T(size=(15,1), k='-FILTER NUMBER-')],
[sg.Button('Run'), sg.B('Edit'), sg.B('Clear'), sg.B('Open Folder'), sg.B('Copy Path')],
@ -474,7 +468,7 @@ def make_window():
[sg.Text('Find (F3):', tooltip=find_re_tooltip), sg.Input(size=(25, 1),key='-FIND RE-', tooltip=find_re_tooltip),sg.B('Find RE')]], k='-RE COL-'))
right_col = [
[sg.Multiline(size=(70, 21), write_only=True, expand_x=True, expand_y=True, key=ML_KEY, reroute_stdout=True, echo_stdout_stderr=True, reroute_cprint=True)],
[sg.Multiline(size=(70, 21), write_only=True, key=ML_KEY, reroute_stdout=True, echo_stdout_stderr=True, reroute_cprint=True)],
[sg.B('Settings'), sg.Button('Exit')],
[sg.T('Demo Browser Ver ' + __version__)],
[sg.T('PySimpleGUI ver ' + sg.version.split(' ')[0] + ' tkinter ver ' + sg.tclversion_detailed, font='Default 8', pad=(0,0))],
@ -485,8 +479,7 @@ def make_window():
options_at_bottom = sg.pin(sg.Column([[sg.CB('Verbose', enable_events=True, k='-VERBOSE-', tooltip='Enable to see the matches in the right hand column'),
sg.CB('Show only first match in file', default=True, enable_events=True, k='-FIRST MATCH ONLY-', tooltip='Disable to see ALL matches found in files'),
sg.CB('Find ignore case', default=True, enable_events=True, k='-IGNORE CASE-'),
sg.CB('Wait for Runs to Complete', default=False, enable_events=True, k='-WAIT-'),
sg.CB('Show ALL file types', default=not python_only, enable_events=True, k='-SHOW ALL FILES-'),
sg.CB('Wait for Runs to Complete', default=False, enable_events=True, k='-WAIT-')
]],
pad=(0,0), k='-OPTIONS BOTTOM-', expand_x=True, expand_y=False), expand_x=True, expand_y=False)
@ -497,15 +490,16 @@ def make_window():
layout = [[sg.Text('PySimpleGUI Demo Program & Project Browser', font='Any 20')],
[choose_folder_at_top],
# [sg.Column([[left_col],[ lef_col_find_re]], element_justification='l', expand_x=True, expand_y=True), sg.Column(right_col, element_justification='c', expand_x=True, expand_y=True)],
[sg.Pane([sg.Column([[left_col],[ lef_col_find_re]], element_justification='l', expand_x=True, expand_y=True), sg.Column(right_col, element_justification='c', expand_x=True, expand_y=True) ], orientation='h', relief=sg.RELIEF_SUNKEN, expand_x=True, expand_y=True, k='-PANE-')],
[options_at_bottom, sg.Sizegrip()]]
[sg.Pane([sg.Column([[left_col],[ lef_col_find_re]], element_justification='l', expand_x=True, expand_y=True), sg.Column(right_col, element_justification='c', expand_x=True, expand_y=True) ], orientation='h', relief=sg.RELIEF_SUNKEN, k='-PANE-')],
[options_at_bottom]]
# --------------------------------- Create Window ---------------------------------
window = sg.Window('PSG Demo & Project Browser', layout, finalize=True, resizable=True, use_default_focus=False, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)
window.set_min_size(window.size)
# window.bind("<Alt_L><x>", 'Exit') # matches the underscore shown on the Exit button (For now disabled this feature until buttons with underscore released to PyPI)
window['-DEMO LIST-'].expand(True, True, True)
window[ML_KEY].expand(True, True, True)
window['-PANE-'].expand(True, True, True)
window.bind('<F1>', '-FOCUS FILTER-')
window.bind('<F2>', '-FOCUS FIND-')
@ -527,12 +521,11 @@ def main():
The main program that contains the event loop.
It will call the make_window function to create the window.
"""
global python_only
try:
version = sg.version
version_parts = version.split('.')
major_version, minor_version = int(version_parts[0]), int(version_parts[1])
if major_version < 4 or (major_version== 4 and minor_version < 32):
if major_version < 4 or minor_version < 32:
sg.popup('Warning - Your PySimpleGUI version is less then 4.35.0',
'As a result, you will not be able to use the EDIT features of this program',
'Please upgrade to at least 4.35.0',
@ -577,16 +570,16 @@ def main():
sg.cprint(f'Editing using {editor_program}', c='white on red', end='')
sg.cprint('')
sg.cprint(f'{full_filename}', c='white on purple')
if not get_editor():
sg.popup_error_with_traceback('No editor has been configured', 'You need to configure an editor in order to use this feature', 'You can configure the editor in the Demo Brower Settings or the PySimpleGUI Global Settings')
# if line != 1:
if using_local_editor():
sg.execute_command_subprocess(editor_program, full_filename)
else:
if using_local_editor():
sg.execute_command_subprocess(editor_program, f'"{full_filename}"')
else:
try:
sg.execute_editor(full_filename, line_number=int(line))
except:
sg.execute_command_subprocess(editor_program, f'"{full_filename}"')
try:
sg.execute_editor(full_filename, line_number=int(line))
except:
sg.execute_command_subprocess(editor_program, full_filename)
# else:
# sg.execute_editor(full_filename)
else:
sg.cprint('Editing canceled')
elif event == 'Run':
@ -724,17 +717,6 @@ def main():
sg.clipboard_set(full_filename)
elif event == 'Version':
sg.popup_scrolled(sg.get_versions(), keep_on_top=True, non_blocking=True)
elif event == '-SHOW ALL FILES-':
python_only = not values[event]
file_list_dict = get_file_list_dict()
file_list = get_file_list()
window['-DEMO LIST-'].update(values=file_list)
window['-FILTER NUMBER-'].update(f'{len(file_list)} files')
window['-ML-'].update('')
window['-FIND NUMBER-'].update('')
window['-FIND-'].update('')
window['-FIND RE-'].update('')
window['-FILTER-'].update('')
window.close()

View file

@ -11,7 +11,7 @@
Displays the values dictionary entry for each element
And more!
Copyright 2021, 2022, 2023 PySimpleGUI
Copyright 2021 PySimpleGUI
"""
import PySimpleGUI as sg
@ -36,7 +36,7 @@ def make_window(theme):
sg.Image(data=sg.DEFAULT_BASE64_LOADING_GIF, enable_events=True, key='-GIF-IMAGE-'),],
[sg.Checkbox('Checkbox', default=True, k='-CB-')],
[sg.Radio('Radio1', "RadioDemo", default=True, size=(10,1), k='-R1-'), sg.Radio('Radio2', "RadioDemo", default=True, size=(10,1), k='-R2-')],
[sg.Combo(values=('Combo 1', 'Combo 2', 'Combo 3'), default_value='Combo 1', readonly=False, k='-COMBO-'),
[sg.Combo(values=('Combo 1', 'Combo 2', 'Combo 3'), default_value='Combo 1', readonly=True, k='-COMBO-'),
sg.OptionMenu(values=('Option 1', 'Option 2', 'Option 3'), k='-OPTION MENU-'),],
[sg.Spin([i for i in range(1,11)], initial_value=10, k='-SPIN-'), sg.Text('Spin')],
[sg.Multiline('Demo of a Multi-Line Text Element!\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nYou get the point.', size=(45,5), expand_x=True, expand_y=True, k='-MLINE-')],
@ -88,17 +88,20 @@ def make_window(theme):
]]
layout[-1].append(sg.Sizegrip())
window = sg.Window('All Elements Demo', layout, right_click_menu=right_click_menu_def, right_click_menu_tearoff=True, grab_anywhere=True, resizable=True, margins=(0,0), use_custom_titlebar=True, finalize=True, keep_on_top=True)
window = sg.Window('All Elements Demo', layout, right_click_menu=right_click_menu_def, right_click_menu_tearoff=True, grab_anywhere=True, resizable=True, margins=(0,0), use_custom_titlebar=True, finalize=True, keep_on_top=True,
# scaling=2.0,
)
window.set_min_size(window.size)
return window
def main():
window = make_window(sg.theme())
# This is an Event Loop
while True:
event, values = window.read(timeout=100)
# keep an animation running so show things are happening
window['-GIF-IMAGE-'].update_animation(sg.DEFAULT_BASE64_LOADING_GIF, time_between_frames=100)
if event not in (sg.TIMEOUT_EVENT, sg.WIN_CLOSED):
print('============ Event = ', event, ' ==============')
print('-------- Values Dictionary (key=value) --------')
@ -107,9 +110,7 @@ def main():
if event in (None, 'Exit'):
print("[LOG] Clicked Exit!")
break
window['-GIF-IMAGE-'].update_animation(sg.DEFAULT_BASE64_LOADING_GIF, time_between_frames=100)
if event == 'About':
elif event == 'About':
print("[LOG] Clicked About!")
sg.popup('PySimpleGUI Demo All Elements',
'Right click anywhere to see right click menu',
@ -150,7 +151,7 @@ def main():
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Versions':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, non_blocking=True)
sg.popup(sg.get_versions(), keep_on_top=True)
window.close()
exit(0)

View file

@ -8,26 +8,18 @@ import PySimpleGUI as sg
Copyright 2022 PySimpleGUI
"""
use_custom_titlebar = True if sg.running_trinket() else False
use_custom_titlebar = False
def make_window(theme=None):
NAME_SIZE = 23
def name(name):
dots = NAME_SIZE-len(name)-2
return sg.Text(name + ' ' + ''*dots, size=(NAME_SIZE,1), justification='r',pad=(0,0), font='Courier 10')
sg.theme(theme)
# NOTE that we're using our own LOCAL Menu element
if use_custom_titlebar:
Menu = sg.MenubarCustom
else:
Menu = sg.Menu
treedata = sg.TreeData()
treedata.Insert("", '_A_', 'Tree Item 1', [1234], )
@ -51,7 +43,7 @@ def make_window(theme=None):
[name('Image'), sg.Image(sg.EMOJI_BASE64_HAPPY_THUMBS_UP)],
[name('Graph'), sg.Graph((125, 50), (0,0), (125,50), k='-GRAPH-')] ]
layout_r = [[name('Canvas'), sg.Canvas(background_color=sg.theme_button_color()[1], size=(125,40))],
layout_r = [[name('Canvas'), sg.Canvas(background_color=sg.theme_button_color()[1], size=(125,50))],
[name('ProgressBar'), sg.ProgressBar(100, orientation='h', s=(10,20), k='-PBAR-')],
[name('Table'), sg.Table([[1,2,3], [4,5,6]], ['Col 1','Col 2','Col 3'], num_rows=2)],
[name('Tree'), sg.Tree(treedata, ['Heading',], num_rows=3)],
@ -67,11 +59,10 @@ def make_window(theme=None):
[name('StatusBar'), sg.StatusBar('StatusBar')],
[name('Sizegrip'), sg.Sizegrip()] ]
# Note - LOCAL Menu element is used (see about for how that's defined)
layout = [[Menu([['File', ['Exit']], ['Edit', ['Edit Me', ]]], k='-CUST MENUBAR-',p=0)],
[sg.T('PySimpleGUI Elements - Use Combo to Change Themes', font='_ 14', justification='c', expand_x=True)],
[sg.Checkbox('Use Custom Titlebar & Menubar', use_custom_titlebar, enable_events=True, k='-USE CUSTOM TITLEBAR-', p=0)],
[sg.Col(layout_l, p=0), sg.Col(layout_r, p=0)]]
layout = [[sg.MenubarCustom([['File', ['Exit']], ['Edit', ['Edit Me', ]]], k='-CUST MENUBAR-',p=0)] if use_custom_titlebar else [sg.Menu([['File', ['Exit']], ['Edit', ['Edit Me', ]]], k='-CUST MENUBAR-',p=0)],
[sg.Checkbox('Use Custom Titlebar & Menubar', use_custom_titlebar, enable_events=True, k='-USE CUSTOM TITLEBAR-')],
[sg.T('PySimpleGUI Elements - Use Combo to Change Themes', font='_ 18', justification='c', expand_x=True)],
[sg.Col(layout_l), sg.Col(layout_r)]]
window = sg.Window('The PySimpleGUI Element List', layout, finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, keep_on_top=True, use_custom_titlebar=use_custom_titlebar)
@ -85,23 +76,20 @@ window = make_window()
while True:
event, values = window.read()
# sg.Print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Edit Me':
sg.execute_editor(__file__)
if values['-COMBO-'] != sg.theme():
sg.theme(values['-COMBO-'])
window.close()
window = make_window()
if event == '-USE CUSTOM TITLEBAR-':
use_custom_titlebar = values['-USE CUSTOM TITLEBAR-']
sg.set_options(use_custom_titlebar=use_custom_titlebar)
window.close()
window = make_window()
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, non_blocking=True)
sg.popup_scrolled(sg.get_versions(), __file__, keep_on_top=True, non_blocking=True)
window.close()

View file

@ -11,8 +11,6 @@ import PySimpleGUI as sg
The first image that uses popup_animated will stop after a few seconds on its own.
The remaining images are shown 1 at a time. To move on to the next image, click the current image.
If you want to exit before reaching the final image, right click the image and choose 'exit'
Copyright 2022 PySimpleGUI
"""
# ---------------------------- Base 64 GIFs ----------------------------
@ -31,20 +29,18 @@ bar_striped = b'R0lGODlhoAAUAIAAAAQCBP7+/iH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCQABACwA
gifs = [ring_blue, red_dots_ring, ring_black_dots, ring_gray_segments, ring_lines, blue_dots, red_dots_ring, bar_striped, line_boxes, line_bubbles]
# first show how to use popup_animated using built-in GIF image
for i in range(1000):
if not sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, message='Right Click To Exit GIF Windows That Follow\nLeft click to move to next one', no_titlebar=False, time_between_frames=100, text_color='black', background_color='white'):
break
for i in range(100000):
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, message='Right Click To Exit GIF Windows That Follow\nLeft click to move to next one', no_titlebar=False, time_between_frames=100, text_color='black', background_color='white')
sg.popup_animated(None) # close all Animated Popups
# Next demo is to show how to create custom windows with animations
layout = [[sg.Image(data=gifs[0], enable_events=True, background_color='white', key='-IMAGE-', right_click_menu=['UNUSED', ['Exit']], pad=0)],]
layout = [[sg.Image(data=gifs[0], enable_events=True, background_color='white', key='-IMAGE-', right_click_menu=['UNUSED', ['Exit']])],]
window = sg.Window('My new window', layout,
no_titlebar=True,
grab_anywhere=True,
keep_on_top=True,
background_color='white',
# transparent_color='white' if sg.running_windows() else None,
alpha_channel=.8,
margins=(0,0))

View file

@ -1,21 +1,22 @@
import PySimpleGUI as sg
import random
"""
Demo - Using a Graph Element to make Bar Charts
The Graph Element is very versatile. Because you can define your own
coordinate system, it makes producing graphs of many lines (bar, line, etc) very
straightforward.
In this Demo a "bar" is nothing more than a rectangle drawn in a Graph Element (draw_rectangle).
To make things a little more interesting, this is a barchart with that data values
placed as labels atop each bar, another Graph element method (draw_text)
Copyright 2022 PySimpleGUI
"""
# Bars drawing in PySimpleGUI
#
# .--.
# | |
# .--.| |.--.
# | || || |
# | || || |
# | || || |
# .--.| || || |
# .--.| || || || |.--.
# | || || || || || |
# | || || || || || |
# .--.| || || || || || |.--.
# | || || || || || || || |.--.
# | || || || || || || || || |
# '--''--''--''--''--''--''--''--''--'
BAR_WIDTH = 50 # width of each bar
@ -37,13 +38,12 @@ while True:
graph.erase()
for i in range(7):
graph_value = random.randint(0, GRAPH_SIZE[1]-25) # choose an int just short of the max value to give room for the label
graph_value = random.randint(0, GRAPH_SIZE[1])
graph.draw_rectangle(top_left=(i * BAR_SPACING + EDGE_OFFSET, graph_value),
bottom_right=(i * BAR_SPACING + EDGE_OFFSET + BAR_WIDTH, 0),
fill_color='green')
# fill_color=sg.theme_button_color()[1])
fill_color=sg.theme_button_color()[1])
graph.draw_text(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10), font='_ 14')
graph.draw_text(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10))
# Normally at the top of the loop, but because we're drawing the graph first, making it at the bottom
event, values = window.read()

File diff suppressed because one or more lines are too long

View file

@ -1,79 +0,0 @@
import PySimpleGUI as sg
"""
Demo Program - Simulated Buttons with Mouseover Highlights
The purpose of this demo is to teach you 5 unique PySimpleGUI constructs that when combined
create a "Button" that highlights on mouseover regarless of the Operating System.
Because of how tktiner works, mouseover highlighting is inconsistent across operating systems for Buttons.
This is one (dare I say "clever") way to get this effect in your program
1. Binding the Enter and Leave tkinter events
2. Using Tuples as keys
3. Using List Comprehensions to build a layout
4. Using Text Elements to Simulate Buttons
5. Using a "User Defined Element" to make what appears to be a new type of Button in the layout
The KEY to making this work simply is these "Buttons" have a tuple as a key.
The format of the key is ('-B-', button_text)
An element's bind method will make a tuple if the original key is a tuple.
(('-B-', button_text), 'ENTER') will be the event when the mouse is moved over the "Button"
Copyright 2022 PySimpleGUI.org
"""
# sg.theme('dark red')
def TextButton(text):
"""
A User Defined Element. It looks like a Button, but is a Text element
:param text: The text that will be put on the "Button"
:return: A Text element with a tuple as the key
"""
return sg.Text(text, key=('-B-', text), relief='raised', enable_events=True, font='_ 15',text_color=sg.theme_button_color_text(), background_color=sg.theme_button_color_background())
def do_binds(window, button_text):
"""
This is magic code that enables the mouseover highlighting to work.
"""
for btext in button_text:
window[('-B-', btext)].bind('<Enter>', 'ENTER')
window[('-B-', btext)].bind('<Leave>', 'EXIT')
def main():
# Defines the text on the 3 buttons we're making
button_text = ('Button 1', 'Button 2', 'Button 3')
# The window's layout
layout = [[TextButton(text) for text in button_text],
[sg.Text(font='_ 14', k='-STATUS-')],
[sg.Ok(), sg.Exit()]]
window = sg.Window('Custom Mouseover Highlighting Buttons', layout, finalize=True)
# After the window is finalized, then can perform the bindings
do_binds(window, button_text)
# The Event Looop
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
# if the event is a tuple, it's one of our TextButtons
if isinstance(event, tuple):
# if second item is one of the bound strings, then do the mouseeover code
if event[1] in ('ENTER', 'EXIT'):
button_key = event[0]
if event[1] == 'ENTER':
window[button_key].update(text_color=sg.theme_button_color_background(), background_color=sg.theme_button_color_text())
if event[1] == 'EXIT':
window[button_key].update(text_color=sg.theme_button_color_text(), background_color=sg.theme_button_color_background())
else: # a "normal" button click (Text clicked) so print the text which we put into the tuple
window['-STATUS-'].update(f'Button pressed = {event[1]}')
window.close()
if __name__ == '__main__':
main()

View file

@ -1,12 +1,5 @@
#!/usr/bin/env python
import PySimpleGUI as sg
"""
Demo - Base64 Buttons with Images
This is perhaps the easiest, quickest, and safest way to use buttons with images in PySimpleGUI.
By putting the button into your code, then you only have to distribute a single file.
Copyright 2022 PySimpleGUI
"""
# First the button images
@ -14,19 +7,24 @@ play = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAA
stop = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAAaklEQVRoge3ZQQqAMAxFwSre/8p6AZFUiXzKzLqLPNJVOwYAvLcVzpztU9Q8zrr/NUW3Y+JsZXsdSjdimY0ISSMkjZA0QtIISSMkjZA0QtIISSMkjZA0QtIISSMkzcxrfMo/ya1lNgIAX1zq+ANHUjXZuAAAAABJRU5ErkJggg=='
eject = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAByklEQVRoge3YO2gUURSA4S+JRnyACIGADyxERAsb0UKrWIidWIidlSA2YpFWSauNVtrYiIU2YpFCLGwEEWwsBAsLEbFQFARFfKBZizkyK5pkZvZmZ7PeH05z595z/sPszpxdMplMJpMZbDZFLGsm8CxiomWXxqzBQ3QiHmNdq0YNGMc9RQOvIjqxNt6iVy1GcF0h/h47sR1vY+0mRluzq8ElhfBn7O9a34tPce1KC161OK8Q/Y7D/7h+EF9jz7k+etXilELwJ44vsO8ofsTeM33wqsURpdzZCvtPK5s+toRetZjCF4XYTI1zM3HmGw4lt6rJbnxQCF1tcP5ynP2IPQm9arENb0LkDsYa5BjFrcjxDjuS2VVkI16EwH2s6iHXStxVvjy39GxXkfV4Iu3Y0T3OPMWGBDkXZDUeRMHnmEyY+/eA2cEjrE2Y+w/GcDsKvcbWJaixGS+jxixWpC4wgmvK+WlX6gJddM9lN6J2Mi4q56cDKRPPwz7lXHYhVdJp5W+KtmK61yZOYG4AGpnDyV6byWT+ZxZ7Rnf6YlGdeX2XxZ8AVag6AiR9uzZg0U/G0NyR3MigUfU7MmhPr78YmjuSyWQymUxmmPgFokSdfYSQKDwAAAAASUVORK5CYII='
sg.theme('Light Green 3')
sg.theme('Light Green 3') # Set a color theme
bg = sg.LOOK_AND_FEEL_TABLE[sg.CURRENT_LOOK_AND_FEEL]['BACKGROUND'] # Get the background for the current theme
# Define the window's layout
layout = [[sg.Button(image_data=play, key='-PLAY-', button_color=sg.theme_background_color(), border_width=0),
sg.Button(image_data=stop, key='-STOP-', button_color=sg.theme_background_color(), border_width=0),
sg.Button(image_data=eject, key='-EXIT-', button_color=sg.theme_background_color(), border_width=0)] ]
layout = [ [sg.Text('Your Application', font='Any 15')],
[sg.Text('Event = '), sg.Text(size=(12,1), key='-OUT-')],
[sg.Button(image_data=play, key='Play', border_width=0, button_color=(bg, bg)),
sg.Button(image_data=stop, key='Stop', button_color=(bg, bg), border_width=0),
sg.Button(image_data=eject, key='Exit', button_color=(bg, bg), border_width=0)] ]
# Create the window
window = sg.Window('Simple Base64 Buttons', layout)
window = sg.Window('Window Title', layout)
while True: # Event Loop
event, values = window.read() # type: str, dict
print(event, values)
if event in (sg.WIN_CLOSED, '-EXIT-'): # If the user exits
if event in (sg.WIN_CLOSED, 'Exit'): # If the user exits
break
window.close() # Exiting so clean up
window['-OUT-'].Update(event) # Output the event to the window
window.close(); del window # Exiting so clean up

View file

@ -26,7 +26,7 @@ def resize_base64_image(image64, size):
'''
image_file = io.BytesIO(base64.b64decode(image64))
img = Image.open(image_file)
img.thumbnail(size, Image.LANCZOS)
img.thumbnail(size, Image.ANTIALIAS)
bio = io.BytesIO()
img.save(bio, format='PNG')
imgbytes = bio.getvalue()

View file

@ -1,26 +0,0 @@
"""
Demo Command Line Application or GUI Application
If your program is run with arguments, then a command line version is used.
If no arguments are given, then a GUI is shown that asks for a filename.
http://www.PySimpleGUI.org
Copyright 2022 PySimpleGUI
"""
import PySimpleGUI as sg
import sys
def main_cli(filename):
print(f'Your filename = {filename}')
def main_gui():
filename = sg.popup_get_file('Please enter a filename:')
main_cli(filename)
if __name__ == '__main__':
if len(sys.argv) < 2:
main_gui()
else:
main_cli(sys.argv[1])

View file

@ -4,28 +4,25 @@ import PySimpleGUI as sg
'''
A simple send/response chat window. Add call to your send-routine and print the response
If async responses can come in, then will need to use a different design that uses PySimpleGUI async design pattern
Copyright 2023 PySimpleGUI
'''
sg.theme('GreenTan') # give our window a spiffy set of colors
layout = [[sg.Text('Your output will go here', size=(40, 1))],
[sg.Output(size=(110, 20), font=('Helvetica 10'))],
[sg.Multiline(size=(70, 5), enter_submits=True, key='-QUERY-', do_not_clear=False),
[sg.Multiline(size=(70, 5), enter_submits=False, key='-QUERY-', do_not_clear=False),
sg.Button('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True),
sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
window = sg.Window('Chat window', layout, font=('Helvetica', ' 13'), default_button_element_size=(8,2), use_default_focus=False)
while True: # The Event Loop
event, values = window.read()
event, value = window.read()
if event in (sg.WIN_CLOSED, 'EXIT'): # quit if exit button or X
break
if event == 'SEND':
query = values['-QUERY-'].rstrip()
query = value['-QUERY-'].rstrip()
# EXECUTE YOUR COMMAND HERE
print('The command you entered was {}'.format(query), flush=True)
window.close()
window.close()

View file

@ -1,45 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Custom Checkboxes done simply
The Base64 Image encoding feature of PySimpleGUI makes it possible to create beautiful GUIs very simply
These 2 checkboxes required 3 extra lines of code than a normal checkbox.
1. Keep track of the current value using the Image Element's Metadata
2. Changle / Update the image when clicked
3. The Base64 image definition
Enable the event on the Image with the checkbox so that you can take action (flip the value)
Copyright 2022 PySimpleGUI
"""
def main():
layout = [[sg.Text('Fancy Checkboxes... Simply')],
[sg.Image(checked, key=('-IMAGE-', 1), metadata=True, enable_events=True), sg.Text(True, enable_events=True, k=('-TEXT-', 1))],
[sg.Image(unchecked, key=('-IMAGE-', 2), metadata=False, enable_events=True), sg.Text(False, enable_events=True, k=('-TEXT-', 2))],
[sg.Button('Go'), sg.Button('Exit')]]
window = sg.Window('Custom Checkboxes', layout, font="_ 14")
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
# if a checkbox is clicked, flip the vale and the image
if event[0] in ('-IMAGE-', '-TEXT-'):
cbox_key = ('-IMAGE-', event[1])
text_key = ('-TEXT-', event[1])
window[cbox_key].metadata = not window[cbox_key].metadata
window[cbox_key].update(checked if window[cbox_key].metadata else unchecked)
# Update the string next to the checkbox
window[text_key].update(window[cbox_key].metadata)
window.close()
if __name__ == '__main__':
checked = b'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'
unchecked = b'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'
main()

View file

@ -4,38 +4,10 @@ import PySimpleGUI as sg
Demo - Class wrapper
Using a class to encapsulate PySimpleGUI Window creation & event loop
This is NOT a recommended design pattern. It mimics the object oriented design that many OO-based
GUI frameworks use, but there is no advantage to structuring you code in his manner. It adds
confusion, not clarity.
The class version is 18 lines of code. The plain version is 13 lines of code.
Two things about the class wrapper jump out as adding confusion:
1. Unneccessary fragmentation of the event loop - the button click code is pulled out of the loop entirely
2. "self" clutters the code without adding value
Copyright 2022, 2023 PySimpleGUI
Copyright 2022 PySimpleGUI
"""
'''
MM'""""'YMM dP
M' .mmm. `M 88
M MMMMMooM 88 .d8888b. .d8888b. .d8888b.
M MMMMMMMM 88 88' `88 Y8ooooo. Y8ooooo.
M. `MMM' .M 88 88. .88 88 88
MM. .dM dP `88888P8 `88888P' `88888P'
MMMMMMMMMMM
M""MMMMM""M oo
M MMMMM M
M MMMMP M .d8888b. 88d888b. .d8888b. dP .d8888b. 88d888b.
M MMMM' .M 88ooood8 88' `88 Y8ooooo. 88 88' `88 88' `88
M MMP' .MM 88. ... 88 88 88 88. .88 88 88
M .dMMM `88888P' dP `88888P' dP `88888P' dP dP
MMMMMMMMMMM
'''
class SampleGUI():
def __init__(self):
@ -63,41 +35,3 @@ class SampleGUI():
my_gui = SampleGUI()
# run the event loop
my_gui.run()
'''
M"""""""`YM dP
M mmmm. M 88
M MMMMM M .d8888b. 88d888b. 88d8b.d8b. .d8888b. 88
M MMMMM M 88' `88 88' `88 88'`88'`88 88' `88 88
M MMMMM M 88. .88 88 88 88 88 88. .88 88
M MMMMM M `88888P' dP dP dP dP `88888P8 dP
MMMMMMMMMMM
M""MMMMM""M oo
M MMMMM M
M MMMMP M .d8888b. 88d888b. .d8888b. dP .d8888b. 88d888b.
M MMMM' .M 88ooood8 88' `88 Y8ooooo. 88 88' `88 88' `88
M MMP' .MM 88. ... 88 88 88 88. .88 88 88
M .dMMM `88888P' dP `88888P' dP `88888P' dP dP
MMMMMMMMMMM
'''
def gui_function():
layout = [ [sg.Text('My layout')],
[sg.Input(key='-IN-')],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('My new window', layout)
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
if event == 'Go':
sg.popup('Go button clicked', 'Input value:', values['-IN-'])
window.close()
gui_function()

View file

@ -1,48 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Preview tkinter cursors
Shows the standard tkinter cursors using Buttons
The name of the cursor is on the Button. Mouse over the Button and you'll see
what that cursor looks like.
This list of cursors is a constant defined in PySimpleGUI. The constant name is:
sg.TKINTER_CURSORS
Copyright 2022 PySimpleGUI
"""
cursors = sg.TKINTER_CURSORS
# Make a layout that's 10 buttons across
NUM_BUTTONS_PER_ROW = 10
layout = [[]]
row = []
for i, c in enumerate(cursors):
# print(i, c)
row.append(sg.Button(c, size=(14,3), k=c))
if ((i+1) % NUM_BUTTONS_PER_ROW) == 0:
layout.append(row)
row = []
# print(row)
# Add on the last, partial row
start = len(cursors)//NUM_BUTTONS_PER_ROW * NUM_BUTTONS_PER_ROW
row = []
for i in range(start, len(cursors)):
row.append(sg.Button(cursors[i], size=(14,3), k=cursors[i]))
layout.append(row)
window = sg.Window('Cursor Previewer',layout, finalize=True)
# set the cursor on each of the buttons that has the name of the cursor as the text
for c in cursors:
window[c].set_cursor(c)
# The ubiquitous event loop...
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
window.close()

View file

@ -9,11 +9,11 @@ import webbrowser
If you want no cursor, set the cursor to the string 'none'.
Copyright 2021, 2022 PySimpleGUI
Copyright 2021 PySimpleGUI
"""
# Here is a more complete list of cursors you can choose from
cursors = sg.TKINTER_CURSORS
cursors = ('X_cursor', 'no', 'arrow','based_arrow_down','based_arrow_up','boat','bogosity','bottom_left_corner','bottom_right_corner','bottom_side','bottom_tee','box_spiral','center_ptr','circle','clock','coffee_mug','cross','cross_reverse','crosshair','diamond_cross','dot','dotbox','double_arrow','draft_large','draft_small','draped_box','exchange','fleur','gobbler','gumby','hand1','hand2','heart','icon','iron_cross','left_ptr','left_side','left_tee','leftbutton','ll_angle','lr_angle','man','middlebutton','mouse','no','pencil','pirate','plus','question_arrow','right_ptr','right_side','right_tee','rightbutton','rtl_logo','sailboat','sb_down_arrow','sb_h_double_arrow','sb_left_arrow','sb_right_arrow','sb_up_arrow','sb_v_double_arrow','shuttle','sizing','spider','spraycan','star','target','tcross','top_left_arrow','top_left_corner','top_right_corner','top_side','top_tee','trek','ul_angle','umbrella','ur_angle','watch','xterm','arrow','center_ptr','crosshair','fleur','ibeam','icon','sb_h_double_arrow','sb_v_double_arrow','watch','xterm','no','starting','size','size_ne_sw','size_ns','size_nw_se','size_we','uparrow','wait','arrow','cross','crosshair','ibeam','plus','watch','xterm')
sg.theme('Light Blue 2')

View file

@ -15,7 +15,7 @@ import psutil
Grab anywhere, making window easy to move around
Note that the keys are tuples, with a tuple as the second item
('-KEY-', (row, col))
Copyright 2020, 2022 PySimpleGUI
Copyright 2020 PySimpleGUI
"""
GRAPH_WIDTH = 120 # each individual graph size in pixels
@ -65,7 +65,8 @@ def main(location):
sg.theme('Black')
layout = [[sg.Text('CPU Core Usage', justification='c', expand_x=True)] ]
layout = [[ sg.Text(sg.SYMBOL_X, enable_events=True, key='Exit', tooltip='Closes window'),
sg.Text(' CPU Core Usage')] ]
# add on the graphs
for rows in range(num_cores//NUM_COLS+1):
@ -85,8 +86,7 @@ def main(location):
element_padding=(0,0),
border_depth=0,
location=location,
enable_close_attempted_event=True,
right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)
right_click_menu=[[''], ['Edit Me', 'Exit',]])
graphs = []
@ -101,15 +101,10 @@ def main(location):
while True :
# --------- Read and update window once every Polling Frequency --------
event, values = window.read(timeout=POLL_FREQUENCY)
if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'): # Be nice and give an exit
sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting
break
elif event == sg.WIN_CLOSED:
if event in (sg.WIN_CLOSED, 'Exit'): # Be nice and give an exit
break
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location())
# read CPU for each core
stats = psutil.cpu_percent(percpu=True)
@ -126,6 +121,5 @@ if __name__ == "__main__":
location = sys.argv[1].split(',')
location = (int(location[0]), int(location[1]))
else:
location = sg.user_settings_get_entry('-location-', (None, None))
location = (None, None)
main(location)

View file

@ -274,7 +274,7 @@ def main(location):
[sg.T(size=(5, 1), font='Any 20', justification='c', background_color='black', k='-gauge VALUE-')]]
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, background_color='black', element_justification='c', finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True)
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, background_color='black', element_justification='c', finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT, enable_close_attempted_event=True)
gauge = Gauge(pointer_color=sg.theme_text_color(), clock_color=sg.theme_text_color(), major_tick_color=sg.theme_text_color(),
minor_tick_color=sg.theme_input_background_color(), pointer_outer_color=sg.theme_text_color(), major_tick_start_radius=45,
@ -298,8 +298,6 @@ def main(location):
break
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
window.close()

View file

@ -23,7 +23,7 @@ def main(location):
layout = [[graph]]
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True)
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT, enable_close_attempted_event=True)
text_id2 = graph.draw_text(f'CPU', (GSIZE[0] // 2, GSIZE[1] // 4), font='Any 20', text_location=sg.TEXT_LOCATION_CENTER, color=sg.theme_button_color()[0])
@ -46,8 +46,6 @@ def main(location):
break
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
# erase figures so they can be redrawn
graph.delete_figure(rect_id)
graph.delete_figure(text_id1)

View file

@ -34,56 +34,41 @@ def main():
# ---------------- Create Form ----------------
sg.theme('Black')
layout = [[sg.Text(font=('Helvetica', 20), text_color=sg.YELLOWS[0], key='-CPU PERCENT-')],
layout = [[sg.Text(font=('Helvetica', 20), text_color=sg.YELLOWS[0], key='-CPU PERCENT-'), sg.Push(), sg.Text(sg.SYMBOL_X, enable_events=True, key='Exit')],
[sg.Text(size=(35, 12), font=('Courier New', 12), key='-PROCESSES-')], # size will determine how many processes shown
[sg.Text('Update every '), sg.Spin([x+1 for x in range(10)], 3, key='-SPIN-'), sg.T('seconds')]]
[sg.Text('Update every '), sg.Spin([x+1 for x in range(10)], 3, key='spin'), sg.T('seconds')]]
window = sg.Window('Top CPU Processes', layout, no_titlebar=True, keep_on_top=True,location=location, use_default_focus=False, alpha_channel=.8, grab_anywhere=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True)
window = sg.Window('Top CPU Processes', layout, no_titlebar=True, keep_on_top=True,location=location, use_default_focus=False, alpha_channel=.8, grab_anywhere=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT, enable_close_attempted_event=True)
# start cpu measurement thread
# using the PySimpleGUI call to start and manage the thread
window.start_thread(lambda: CPU_thread(window), '-THREAD FINISHED-')
window.perform_long_operation(lambda: CPU_thread(window), '-THREAD FINISHED-')
g_interval = 1
# Unusual construct of a Try around entire event loop... something is crashing, we need to find out what...
try:
# ---------------- main loop ----------------
while True:
# --------- Read and update window --------
event, values = window.read()
# print(event, values)
# --------- Do Button Operations --------
if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting
break
if event == 'Edit Me':
sp = sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location())
elif event == '-CPU UPDATE FROM THREAD-': # indicates data from the thread has arrived
cpu_percent, procs = values[event] # the thread sends a tuple
if procs:
# --------- Create dictionary of top % CPU processes. Format is name:cpu_percent --------
top = {}
for proc in procs:
try:
top[proc.name()] = proc.cpu_percent()
except Exception as e:
pass # it's OK to get an exception here because processes come and go... one may have gone...
# sg.Print('*** GOT Exception looping through procs ***', c='white on red', font='_ 18')
# sg.Print('Exception = ', e, 'procs=', procs, 'proc', proc)
# ---------------- main loop ----------------
while True:
# --------- Read and update window --------
event, values = window.read()
# --------- Do Button Operations --------
if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting
break
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == '-CPU UPDATE FROM THREAD-': # indicates data from the thread has arrived
cpu_percent, procs = values[event] # the thread sends a tuple
if procs:
# --------- Create dictionary of top % CPU processes. Format is name:cpu_percent --------
top = {proc.name(): proc.cpu_percent() for proc in procs}
top_sorted = sorted(top.items(), key=operator.itemgetter(1), reverse=True) # reverse sort to get highest CPU usage on top
if top_sorted:
top_sorted.pop(0) # remove the idle process
display_string = '\n'.join([f'{cpu/10:2.2f} {proc:23}' for proc, cpu in top_sorted])
# --------- Display timer and proceses in window --------
window['-CPU PERCENT-'].update(f'CPU {cpu_percent}')
window['-PROCESSES-'].update(display_string)
# get the timeout from the spinner
g_interval = int(values['-SPIN-'])
except Exception as e:
sg.Print('*** GOT Exception in event loop ***', c='white on red', font='_ 18')
sg.Print('Exception = ', e, wait=True) # IMPORTANT to add a wait/blocking so that the print pauses execution. Otherwise program continue and exits
top_sorted = sorted(top.items(), key=operator.itemgetter(1), reverse=True) # reverse sort to get highest CPU usage on top
if top_sorted:
top_sorted.pop(0) # remove the idle process
display_string = '\n'.join([f'{cpu/10:2.2f} {proc:23}' for proc, cpu in top_sorted])
# --------- Display timer and proceses in window --------
window['-CPU PERCENT-'].update(f'CPU {cpu_percent}')
window['-PROCESSES-'].update(display_string)
# get the timeout from the spinner
g_interval = int(values['spin'])
window.close()

View file

@ -1,12 +1,8 @@
import PySimpleGUI as sg
import datetime
import PIL
from PIL import Image
import PIL.Image, PIL.ImageTk
import random
import os
import io
import base64
"""
Another simple Desktop Widget using PySimpleGUI
@ -20,76 +16,17 @@ import base64
* How long to show the image and if you wnt this time to vary semi-randomly
* Folder containing your images
Copyright 2021, 2023 PySimpleGUI
Copyright 2021 PySimpleGUI
"""
ALPHA = 0.9 # Initial alpha until user changes
refresh_font = sg.user_settings_get_entry('-refresh font-', 'Courier 8')
def make_square(im, fill_color=(0, 0, 0, 0)):
x, y = im.size
size = max(x, y)
new_im = Image.new('RGBA', (size, size), fill_color)
new_im.paste(im, (int((size - x) / 2), int((size - y) / 2)))
return new_im
def get_image_size(source):
if isinstance(source, str):
image = PIL.Image.open(source)
elif isinstance(source, bytes):
image = PIL.Image.open(io.BytesIO(base64.b64decode(source)))
else:
image = PIL.Image.open(io.BytesIO(source))
width, height = image.size
return (width, height)
def convert_to_bytes(source, size=(None, None), subsample=None, zoom=None, fill=False):
"""
Will convert into bytes and optionally resize an image that is a file or a base64 bytes object.
Turns into PNG format in the process so that can be displayed by tkinter
:param source: either a string filename or a bytes base64 image object
:type source: (Union[str, bytes])
:param size: optional new size (width, height)
:type size: (Tuple[int, int] or None)
:param subsample: change the size by multiplying width and height by 1/subsample
:type subsample: (int)
:param zoom: change the size by multiplying width and height by zoom
:type zoom: (int)
:param fill: If True then the image is filled/padded so that the image is square
:type fill: (bool)
:return: (bytes) a byte-string object
:rtype: (bytes)
"""
# print(f'converting {source} {size}')
if isinstance(source, str):
image = PIL.Image.open(source)
elif isinstance(source, bytes):
image = PIL.Image.open(io.BytesIO(base64.b64decode(source)))
else:
image = PIL.Image.open(io.BytesIO(source))
width, height = image.size
scale = None
if size != (None, None):
new_width, new_height = size
scale = min(new_height/height, new_width/width)
elif subsample is not None:
scale = 1/subsample
elif zoom is not None:
scale = zoom
resized_image = image.resize((int(width * scale), int(height * scale)), Image.LANCZOS) if scale is not None else image
if fill and scale is not None:
resized_image = make_square(resized_image)
# encode a PNG formatted version of image into BASE64
with io.BytesIO() as bio:
resized_image.save(bio, format="PNG")
contents = bio.getvalue()
encoded = base64.b64encode(contents)
return encoded
def convert_to_bytes(file_or_bytes, resize=None):
image = PIL.Image.open(file_or_bytes)
image.thumbnail(resize)
photo_img = PIL.ImageTk.PhotoImage(image)
return photo_img
def choose_theme(location):
@ -106,15 +43,6 @@ def choose_theme(location):
else:
return None
def reset_settings():
sg.user_settings_set_entry('-time per image-', 60)
sg.user_settings_set_entry('-random time-', False)
sg.user_settings_set_entry('-image size-', (None, None))
sg.user_settings_set_entry('-image_folder-', None)
sg.user_settings_set_entry('-location-', (None, None))
sg.user_settings_set_entry('-single image-', None)
sg.user_settings_set_entry('-alpha-', ALPHA)
def make_window(location):
alpha = sg.user_settings_get_entry('-alpha-', ALPHA)
@ -133,7 +61,7 @@ def make_window(location):
layout = [[sg.Image(k='-IMAGE-', enable_events=True)],
[sg.pin(sg.Column(refresh_info, key='-REFRESH INFO-', element_justification='c', visible=sg.user_settings_get_entry('-show refresh-', True)))]]
window = sg.Window('Photo Frame', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_justification='c', element_padding=(0, 0), alpha_channel=alpha, finalize=True, right_click_menu=right_click_menu, keep_on_top=True, enable_close_attempted_event=True, enable_window_config_events=True)
window = sg.Window('Photo Frame', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_justification='c', element_padding=(0, 0), alpha_channel=alpha, finalize=True, right_click_menu=right_click_menu, keep_on_top=True, enable_close_attempted_event=True)
return window
@ -141,10 +69,11 @@ def make_window(location):
def main():
loc = sg.user_settings_get_entry('-location-', (None, None))
sg.theme(sg.user_settings_get_entry('-theme-', None))
window = make_window(loc)
time_per_image = sg.user_settings_get_entry('-time per image-', 60)
vary_randomly = sg.user_settings_get_entry('-random time-', False)
width, height = sg.user_settings_get_entry('-image size-', (None, None))
width, height = sg.user_settings_get_entry('-image size-', (400,300))
image_folder = sg.user_settings_get_entry('-image_folder-', None)
try:
@ -153,26 +82,36 @@ def main():
image_folder = None
sg.user_settings_set_entry('-image_folder-', None)
image_name = single_image = sg.user_settings_get_entry('-single image-', None)
single_image = sg.user_settings_get_entry('-single image-', None)
if image_folder is None and single_image is None:
image_name = single_image = sg.popup_get_file('Choose a starting image', keep_on_top=True)
if not single_image:
if sg.popup_yes_no('No folder entered','Go you want to exit the program entirely?', keep_on_top=True) == 'Yes':
exit()
if image_folder is not None and single_image is None:
while True:
images = None
image_folder = sg.popup_get_folder('Choose location of your images', location=window.current_location(), keep_on_top=True)
if image_folder is not None:
sg.user_settings_set_entry('-image_folder-', image_folder)
break
else:
if sg.popup_yes_no('No folder entered','Go you want to exit the program entirely?', keep_on_top=True) == 'Yes':
exit()
elif single_image is None:
images = os.listdir(image_folder)
images = [i for i in images if i.lower().endswith(('.png', '.jpg', '.gif'))]
image_name = os.path.join(image_folder, random.choice(images))
else: # means single image is not none
images = None
image_name = single_image
window = make_window(loc)
window_size = window.size
image_data = convert_to_bytes(image_name, (width, height))
while True: # Event Loop
# First update the status information
# for debugging show the last update date time
if single_image is None:
image_name =random.choice(images)
image_data = convert_to_bytes(os.path.join(image_folder, image_name), (width, height))
window['-FOLDER-'].update(image_folder)
else:
image_name = single_image
image_data = convert_to_bytes(single_image, (width, height))
window['-FILENAME-'].update(image_name)
window['-IMAGE-'].update(data=image_data)
window['-REFRESHED-'].update(datetime.datetime.now().strftime("%m/%d/%Y %I:%M:%S %p"))
# -------------- Start of normal event loop --------------
timeout = time_per_image * 1000 + (random.randint(int(-time_per_image * 500), int(time_per_image * 500)) if vary_randomly else 0) if single_image is None else None
event, values = window.read(timeout=timeout)
@ -181,28 +120,6 @@ def main():
elif event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
break
# First update the status information
# for debugging show the last update date time
if event == sg.TIMEOUT_EVENT:
if single_image is None:
image_name =random.choice(images)
image_data = convert_to_bytes(os.path.join(image_folder, image_name))
window['-FOLDER-'].update(image_folder)
else:
image_name = single_image
image_data = convert_to_bytes(single_image, (width, height))
window['-FILENAME-'].update(image_name)
window['-IMAGE-'].update(data=image_data)
window['-REFRESHED-'].update(datetime.datetime.now().strftime("%m/%d/%Y %I:%M:%S %p"))
if event == sg.WINDOW_CONFIG_EVENT:
new_size = window.size
if new_size != window_size:
print(f'resizing {new_size}')
(width, height) = new_size
image_data = convert_to_bytes(image_data, (width, height))
window['-IMAGE-'].update(data=image_data)
window.size = get_image_size(image_data)
window_size = window.size
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Choose Image Folder':
@ -258,15 +175,12 @@ def main():
window.close()
window = make_window(loc)
elif event == 'Choose Single Image':
image_name = single_image = sg.popup_get_file('Choose single image to show', history=True)
single_image = sg.popup_get_file('Choose single image to show', history=True)
sg.user_settings_set_entry('-single image-', single_image)
(width, height) = get_image_size(single_image)
sg.user_settings_set_entry('-image size-', (width, height))
image_data = convert_to_bytes(image_name, (width, height))
window['-IMAGE-'].update(data=image_data)
window.size = window_size = (width, height)
window.close()
if __name__ == '__main__':
# reset_settings() # if get corrupted problems, uncomment this
main()

View file

@ -7,7 +7,6 @@ import sys
Desktop "Rainmeter" style widget - Drive usage
Requires: psutil
Shows a bar graph of space used for each drive partician that psutil finds
Copyright 2022 PySimpleGUI
"""
ALPHA = 0.7
@ -17,21 +16,13 @@ UPDATE_FREQUENCY_MILLISECONDS = 20 * 1000
BAR_COLORS = ('#23a0a0', '#56d856', '#be45be', '#5681d8', '#d34545', '#BE7C29')
class Globals():
drive_list = None
def __init__(self):
return
def human_size(bytes, units=(' bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
""" Returns a human readable string reprentation of bytes"""
return str(bytes) + ' ' + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:])
def update_window(window):
drive_list = []
particians = psutil.disk_partitions()
all_ok = True
for count, part in enumerate(particians):
mount = part[0]
try:
@ -40,19 +31,15 @@ def update_window(window):
window[('-PROG-', mount)].update_bar(int(usage.percent))
window[('-%-', mount)].update(f'{usage.percent}%')
window[('-STATS-', mount)].update(f'{human_size(usage.used)} / {human_size(usage.total)} = {human_size(usage.free)} free')
drive_list.append(str(mount))
except KeyError as e: # A key error means a new drive was added
all_ok = False
except Exception as e:
except:
pass
all_ok = Globals.drive_list == drive_list and all_ok
Globals.drive_list = drive_list
return all_ok
def main(location):
sg.theme(THEME)
# ---------------- Create Layout ----------------
def create_window(location):
layout = [[sg.Text('Drive Status', font='Any 16')]]
# Add a row for every partician that has a bar graph and text stats
@ -69,48 +56,20 @@ def create_window(location):
sg.Text(f'{usage.percent}%', size=(6, 1), key=('-%-', mount)), sg.T(stats_info, size=(30, 1), key=('-STATS-', mount))]]
except:
pass
layout += [[sg.Text('Refresh', font='Any 8', key='-REFRESH-', enable_events=True)]]
layout += [[sg.Text('Refresh', font='Any 8', key='-REFRESH-', enable_events=True), sg.Text('', enable_events=True, key='Exit Text')]]
# ---------------- Create Window ----------------
window = sg.Window('Drive Status Widget', layout, location=location, keep_on_top=True, grab_anywhere=True, no_titlebar=True, alpha_channel=ALPHA, use_default_focus=False,right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT,
finalize=True, enable_close_attempted_event=True)
window = sg.Window('Drive Status Widget', layout, location=location, keep_on_top=True, grab_anywhere=True, no_titlebar=True, alpha_channel=ALPHA, use_default_focus=False,
finalize=True)
return window
def main(location):
# we rely on a key error to tell us if a drive was added. So.... we don't want pesky popups or other key erros to be shown
sg.set_options(suppress_error_popups=True, suppress_raise_key_errors=False, suppress_key_guessing=True)
sg.theme(THEME)
window = create_window(location)
update_window(window) # sets the progress bars
try:
# ---------------- Event Loop ----------------
while True:
event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS)
if event in (sg.WIN_CLOSED, sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
if event != sg.WIN_CLOSED:
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
break
if event == 'Edit Me':
sp = sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location())
if not update_window(window): # update the window.. if not True then something changed and need to make a new window
window.close()
window = create_window(location)
update_window(window)
except Exception as e:
sg.Print('ERROR in event loop', e)
sg.popup_error_with_traceback('Crashed', e)
sg.popup('Check the error!')
# ---------------- Event Loop ----------------
while True:
event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS)
if event == sg.WIN_CLOSED or event.startswith('Exit'):
break
update_window(window)
if __name__ == '__main__':
@ -118,6 +77,6 @@ if __name__ == '__main__':
location = sys.argv[1].split(',')
location = (int(location[0]), int(location[1]))
else:
location = sg.user_settings_get_entry('-location-', (None, None))
location = (None, None)
main(location)

View file

@ -275,7 +275,7 @@ def main(location):
[sg.T(size=(8, 1), font='Any 14', justification='c', background_color='black', k='-RAM USED-')],
]
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, background_color='black', element_justification='c', finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True)
window = sg.Window('CPU Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, background_color='black', element_justification='c', finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT)
gauge = Gauge(pointer_color=sg.theme_text_color(), clock_color=sg.theme_text_color(), major_tick_color=sg.theme_text_color(),
minor_tick_color=sg.theme_input_background_color(), pointer_outer_color=sg.theme_text_color(), major_tick_start_radius=45,
@ -290,7 +290,7 @@ def main(location):
if gauge.change():
new_angle = ram_percent*180/100
window['-gauge VALUE-'].update(f'{ram_percent}%')
window['-gauge VALUE-'].update(f'{ram_percent}')
window['-RAM USED-'].update(f'{human_size(ram.used)}')
gauge.change(degree=new_angle, step=180)
gauge.change()
@ -298,13 +298,10 @@ def main(location):
# update the window, wait for a while, then check for exit
event, values = window.read(timeout=UPDATE_FREQUENCY_MILLISECONDS)
if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
window.close()
@ -314,5 +311,5 @@ if __name__ == '__main__':
location = sys.argv[1].split(',')
location = (int(location[0]), int(location[1]))
else:
location = sg.user_settings_get_entry('-location-', (None, None))
location = (None, None)
main(location)

View file

@ -30,7 +30,7 @@ def main(location):
graph = sg.Graph(GSIZE, (0, 0), GSIZE, key='-GRAPH-', enable_events=True)
layout = [[graph]]
window = sg.Window('RAM Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT, enable_close_attempted_event=True, keep_on_top=True)
window = sg.Window('RAM Usage Widget Square', layout, location=location, no_titlebar=True, grab_anywhere=True, margins=(0, 0), element_padding=(0, 0), alpha_channel=ALPHA, finalize=True, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT, enable_close_attempted_event=True)
while True: # Event Loop
@ -47,10 +47,8 @@ def main(location):
if event != sg.WIN_CLOSED:
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
break
if event == 'Edit Me':
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
graph.delete_figure(rect_id)
graph.delete_figure(text_id1)

View file

@ -29,7 +29,7 @@ def main():
layout = [[sg.Image(blank, key=('-IMAGE-', i), p=0, subsample=subsample) for i in range(max_digits)]]
window = sg.Window('', layout, background_color='black', no_titlebar=True, grab_anywhere=True, right_click_menu=right_click_menu, location=location, keep_on_top=True, enable_close_attempted_event=True, alpha_channel=alpha, metadata=subsample)
window = sg.Window('', layout, background_color='black', no_titlebar=True, grab_anywhere=True, right_click_menu=right_click_menu, location=location, enable_close_attempted_event=True, alpha_channel=alpha, metadata=subsample)
while True:
event, values = window.read(timeout=300)

View file

@ -1,74 +0,0 @@
#!/usr/bin/env python
import PySimpleGUI as sg
import time
"""
Demo Program - Timer Desktop Widget using Window.timer_start and Window.timer_stop
This is a re-implementation of the original timer desktop widget that used window.read timeouts as
the means of getting timer events.
This program uses the new Window.timer_start to get timer events. It is simpler because:
There is only 1 call to window.read and it's in the standard location in the event loop
The timer pause/run button uses the timer_start and timer_stop calls - perhaps more intuitive
Note that this Demo Program requires PySimpleGUI 4.60.4.132 and greater
Copyright 2022 PySimpleGUI
"""
def time_as_int():
return int(round(time.time() * 100))
# ---------------- Create Form ----------------
sg.theme('Black')
layout = [[sg.Text('')],
[sg.Text('', size=(8, 2), font=('Helvetica', 20),
justification='center', key='text')],
[sg.Button('Pause', key='-RUN-PAUSE-', button_color=('white', '#001480')),
sg.Button('Reset', button_color=('white', '#007339'), key='-RESET-'),
sg.Exit(button_color=('white', 'firebrick4'), key='Exit')]]
window = sg.Window('Running Timer', layout,
no_titlebar=True,
auto_size_buttons=False,
keep_on_top=True,
grab_anywhere=True,
element_padding=(0, 0),
finalize=True,
element_justification='c',
right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_EXIT)
current_time, paused_time, paused = 0, 0, False
start_time = time_as_int()
timer_id = window.timer_start(10)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'): # ALWAYS give a way out of program
break
# --------- Handle events --------
if event == '-RUN-PAUSE-':
paused = not paused
if paused:
window.timer_stop(timer_id)
paused_time = time_as_int()
else:
timer_id = window.timer_start(10)
start_time = start_time + time_as_int() - paused_time
window['-RUN-PAUSE-'].update('Run' if paused else 'Pause')
elif event == sg.EVENT_TIMER:
current_time = time_as_int() - start_time
if event == '-RESET-':
current_time = 0
start_time = paused_time = time_as_int()
elif event == 'Edit Me':
sg.execute_editor(__file__)
# --------- Display timer_id in window --------
window['text'].update('{:02d}:{:02d}.{:02d}'.format((current_time // 100) // 60,
(current_time // 100) % 60,
current_time % 100))
window.close()

View file

@ -75,9 +75,8 @@ def change_settings(settings, window_location=(None, None)):
nearest_postal = ''
layout = [[sg.T('Enter Zipcode or City for your location')],
[sg.I(settings.get('-location-', nearest_postal), size=(15, 1), key='-LOCATION-'), sg.T('City')],
[sg.I(settings.get('-country-', 'US'), size=(15, 1), key='-COUNTRY-'), sg.T('Country')],
[sg.I(settings.get('-friends name-', ''), size=(15, 1), key='-FRIENDS NAME-'), sg.T('Who')],
[sg.I(settings.get('-location-', nearest_postal), size=(15, 1), key='-LOCATION-')],
[sg.I(settings.get('-country-', 'US'), size=(15, 1), key='-COUNTRY-')],
[sg.I(settings.get('-api key-', ''), size=(32, 1), key='-API KEY-')],
[sg.CBox('Use Metric For Temperatures', default=settings.get('-celsius-', False),key='-CELSIUS-')],
[sg.B('Ok', border_width=0, bind_return_key=True), sg.B('Register For a Key', border_width=0, k='-REGISTER-'), sg.B('Cancel', border_width=0)], ]
@ -97,7 +96,6 @@ def change_settings(settings, window_location=(None, None)):
settings['-country-'] = values['-COUNTRY-']
API_KEY = settings['-api key-'] = values['-API KEY-']
settings['-celsius-'] = values['-CELSIUS-']
settings['-friends name-'] = values['-FRIENDS NAME-']
else:
API_KEY = settings['-api key-']
user_location = settings['-location-']
@ -188,42 +186,43 @@ def metric_row(metric):
sg.Text(APP_DATA[metric], font=('Arial', 10, 'bold'), pad=(0, 0), size=(9, 1), key=metric)]
def create_window(win_location, settings):
def create_window(win_location):
""" Create the application window """
friends_name = settings.get('-friends name-', '')
col1 = sg.Column(
[[sg.Text(APP_DATA['City'], font=('Arial Rounded MT Bold', 18), background_color=BG_COLOR, text_color=TXT_COLOR, key='City'),
sg.Text(f' - {friends_name}' if friends_name else '', background_color=BG_COLOR, text_color=TXT_COLOR, font=('Arial Rounded MT Bold', 18),)],
[[sg.Text(APP_DATA['City'], font=('Arial Rounded MT Bold', 18), pad=((10, 0), (50, 0)), size=(18, 1), background_color=BG_COLOR, text_color=TXT_COLOR, key='City')],
[sg.Text(APP_DATA['Description'], font=('Arial', 12), pad=(10, 0), background_color=BG_COLOR, text_color=TXT_COLOR, key='Description')]],
background_color=BG_COLOR, key='COL1')
col2 = sg.Column([[sg.Image(data=APP_DATA['Icon'], size=(100, 100), background_color=BG_COLOR, key='Icon')]],
element_justification='center', background_color=BG_COLOR, key='COL2')
col2 = sg.Column(
[[sg.Text('×', font=('Arial Black', 16), pad=(0, 0), justification='right', background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-QUIT-')],
[sg.Image(data=APP_DATA['Icon'], pad=((5, 10), (0, 0)), size=(100, 100), background_color=BG_COLOR, key='Icon')]],
element_justification='center', background_color=BG_COLOR, key='COL2')
col3 = sg.Column([[sg.Text(APP_DATA['Updated'], font=('Arial', 8), background_color=BG_COLOR, text_color=TXT_COLOR, key='Updated')]],
pad=(10, 5), element_justification='left', background_color=BG_COLOR, key='COL3')
col3 = sg.Column(
[[sg.Text(APP_DATA['Updated'], font=('Arial', 8), background_color=BG_COLOR, text_color=TXT_COLOR, key='Updated')]],
pad=(10, 5), element_justification='left', background_color=BG_COLOR, key='COL3')
col4 = sg.Column(
[[sg.Text('Settings', font=('Arial', 8, 'italic'), background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-CHANGE-'),
sg.Text('Refresh', font=('Arial', 8, 'italic'), background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-REFRESH-')]],
pad=(10, 5), element_justification='right', background_color=BG_COLOR, key='COL4')
top_col = sg.Column([[col1, sg.Push(background_color=BG_COLOR), col2, sg.Text('×', font=('Arial Black', 16), pad=(0, 0), justification='right', background_color=BG_COLOR, text_color=TXT_COLOR, enable_events=True, key='-QUIT-')]], pad=(0, 0), background_color=BG_COLOR, key='TopCOL')
top_col = sg.Column([[col1, col2]], pad=(0, 0), background_color=BG_COLOR, key='TopCOL')
bot_col = sg.Column([[col3, col4]],
pad=(0, 0), background_color=BG_COLOR, key='BotCOL')
bot_col = sg.Column([[col3, col4]], pad=(0, 0), background_color=BG_COLOR, key='BotCOL')
lf_col = sg.Column(
[[sg.Text(APP_DATA['Temp'], font=('Haettenschweiler', 90), pad=((10, 0), (0, 0)), justification='center', key='Temp')]],
pad=(10, 0), element_justification='center', key='LfCOL')
rt_col = sg.Column([metric_row('Feels Like'), metric_row('Wind'), metric_row('Humidity'), metric_row('Precip 1hr'), metric_row('Pressure')],
pad=((15, 0), (25, 5)), key='RtCOL')
rt_col = sg.Column(
[metric_row('Feels Like'), metric_row('Wind'), metric_row('Humidity'), metric_row('Precip 1hr'), metric_row('Pressure')],
pad=((15, 0), (25, 5)), key='RtCOL')
layout = [[top_col],
[lf_col, rt_col],
[bot_col],
[sg.Text(f'PSG: {sg.ver} Tk:{sg.framework_version} Py:{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}', font=('Arial', 8), justification='c', background_color=BG_COLOR, text_color=TXT_COLOR, pad=(0,0), expand_x=True)]]
[sg.Text(f'{sg.ver} {sg.framework_version} {sys.version}', font=('Arial', 8), background_color=BG_COLOR, text_color=TXT_COLOR, pad=(0,0))]]
window = sg.Window(layout=layout, title='Weather Widget', margins=(0, 0), finalize=True, location=win_location,
element_justification='center', keep_on_top=True, no_titlebar=True, grab_anywhere=True, alpha_channel=ALPHA,
@ -278,36 +277,28 @@ def main(refresh_rate, win_location):
sg.popup_error('Having trouble with location. Your location: ', location)
exit()
window = create_window(win_location, settings)
window = create_window(win_location)
while True: # Event Loop
event, values = window.read(timeout=refresh_in_milliseconds)
if event in (None, '-QUIT-', 'Exit', sg.WIN_CLOSE_ATTEMPTED_EVENT):
sg.user_settings_set_entry('-win location-', window.current_location()) # The line of code to save the position before exiting
break
try:
if event == '-CHANGE-':
x, y = window.current_location()
settings = change_settings(settings, (x + 200, y+50))
window.close()
window = create_window(win_location, settings)
elif event == '-REFRESH-':
sg.popup_quick_message('Refreshing...', keep_on_top=True, background_color='red', text_color='white',
auto_close_duration=3, non_blocking=False, location=(window.current_location()[0]+window.size[0]//2-30, window.current_location()[1]+window.size[1]//2-10))
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Versions':
sg.main_get_debug_data()
elif event != sg.TIMEOUT_KEY:
sg.Print('Unknown event received\nEvent & values:\n', event, values, location=win_location)
if event == '-CHANGE-':
x, y = window.current_location()
settings = change_settings(settings, (x + 200, y+50))
elif event == '-REFRESH-':
sg.popup_quick_message('Refreshing...', keep_on_top=True, background_color='red', text_color='white',
auto_close_duration=3, non_blocking=False, location=(window.current_location()[0]+window.size[0]//2-30, window.current_location()[1]+window.size[1]//2-10))
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Versions':
sg.main_get_debug_data()
elif event != sg.TIMEOUT_KEY:
sg.Print('Unknown event received\nEvent & values:\n', event, values, location=win_location)
update_weather()
update_metrics(window)
except Exception as e:
sg.Print('*** GOT Exception in event loop ***', c='white on red', location=window.current_location(), keep_on_top=True)
sg.Print('File = ', __file__, f'Window title: {window.Title}')
sg.Print('Exception = ', e, wait=True) # IMPORTANT to add a wait/blocking so that the print pauses execution. Otherwise program continue and exits
update_weather()
update_metrics(window)
window.close()

View file

@ -1,6 +1,7 @@
#!/usr/bin/env python
import PySimpleGUI as sg
import psutil
import sys
"""
Desktop floating widget - System status dashboard
@ -10,7 +11,6 @@ import psutil
CPU Used
Mem Used
Information is updated once a second and is shown as an area graph that scrolls
Copyright 2022 PySimpleGUI
"""
GRAPH_WIDTH, GRAPH_HEIGHT = 120, 40 # each individual graph size in pixels
@ -53,11 +53,10 @@ def human_size(bytes, units=(' bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
return str(bytes) + units[0] if bytes < 1024 else human_size(bytes >> 10, units[1:])
def main():
def main(location):
# ---------------- Create Window ----------------
sg.theme('Black')
sg.set_options(element_padding=(0, 0), margins=(1, 1), border_width=0)
location = sg.user_settings_get_entry('-location-', (None, None))
def GraphColumn(name, key):
layout = [
@ -69,8 +68,10 @@ def main():
key=key+'GRAPH_')]]
return sg.Col(layout, pad=(2, 2))
red_x = b"R0lGODlhEAAQAPeQAIsAAI0AAI4AAI8AAJIAAJUAAJQCApkAAJoAAJ4AAJkJCaAAAKYAAKcAAKcCAKcDA6cGAKgAAKsAAKsCAKwAAK0AAK8AAK4CAK8DAqUJAKULAKwLALAAALEAALIAALMAALMDALQAALUAALYAALcEALoAALsAALsCALwAAL8AALkJAL4NAL8NAKoTAKwbAbEQALMVAL0QAL0RAKsREaodHbkQELMsALg2ALk3ALs+ALE2FbgpKbA1Nbc1Nb44N8AAAMIWAMsvAMUgDMcxAKVABb9NBbVJErFYEq1iMrtoMr5kP8BKAMFLAMxKANBBANFCANJFANFEB9JKAMFcANFZANZcANpfAMJUEMZVEc5hAM5pAMluBdRsANR8AM9YOrdERMpIQs1UVMR5WNt8X8VgYMdlZcxtYtx4YNF/btp9eraNf9qXXNCCZsyLeNSLd8SSecySf82kd9qqc9uBgdyBgd+EhN6JgtSIiNuJieGHhOGLg+GKhOKamty1ste4sNO+ueenp+inp+HHrebGrefKuOPTzejWzera1O7b1vLb2/bl4vTu7fbw7ffx7vnz8f///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAJAALAAAAAAQABAAAAjUACEJHEiwYEEABniQKfNFgQCDkATQwAMokEU+PQgUFDAjjR09e/LUmUNnh8aBCcCgUeRmzBkzie6EeQBAoAAMXuA8ciRGCaJHfXzUMCAQgYooWN48anTokR8dQk4sELggBhQrU9Q8evSHiJQgLCIIfMDCSZUjhbYuQkLFCRAMAiOQGGLE0CNBcZYmaRIDLqQFGF60eTRoSxc5jwjhACFWIAgMLtgUocJFy5orL0IQRHAiQgsbRZYswbEhBIiCCH6EiJAhAwQMKU5DjHCi9gnZEHMTDAgAOw=="
layout = [
[sg.Text('System Status Dashboard'+' '*18)],
[sg.Text('System Status Dashboard'+' '*18),
sg.Button('', image_data=red_x, button_color=('black', 'black'), key='Exit', tooltip='Closes window')],
[GraphColumn('Net Out', '_NET_OUT_'),
GraphColumn('Net In', '_NET_IN_')],
[GraphColumn('Disk Read', '_DISK_READ_'),
@ -81,8 +82,8 @@ def main():
window = sg.Window('PSG System Dashboard', layout,
keep_on_top=True,
grab_anywhere=True, no_titlebar=True,
return_keyboard_events=True, alpha_channel=ALPHA, enable_close_attempted_event=True,
use_default_focus=False, finalize=True, location=location,right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT,)
return_keyboard_events=True, alpha_channel=ALPHA,
use_default_focus=False, finalize=True, location=location)
# setup graphs & initial values
netio = psutil.net_io_counters()
@ -103,14 +104,9 @@ def main():
while True :
# --------- Read and update window once a second--------
event, values = window.read(timeout=1000)
if event in (sg.WIN_CLOSE_ATTEMPTED_EVENT, 'Exit'):
sg.user_settings_set_entry('-location-', window.current_location()) # save window location before exiting
# Be nice and give an exit, expecially since there is no titlebar
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Edit Me':
sp = sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), keep_on_top=True, location=window.current_location())
# ----- Network Graphs -----
netio = psutil.net_io_counters()
write_bytes = net_graph_out.graph_value(netio.bytes_sent)
@ -133,5 +129,10 @@ def main():
window['_MEM_TXT_'].update('{}% Memory Used'.format(mem_used))
if __name__ == '__main__':
main()
if len(sys.argv) > 1:
location = sys.argv[1].split(',')
location = (int(location[0]), int(location[1]))
else:
location = (None, None)
main(location)

View file

@ -12,7 +12,6 @@
* If-Else
* Dictionaries
* Functions as keys
* Lambda as key (callable like functions are)
The handlers in this demo are all functions that are called once the event is detected
@ -23,7 +22,7 @@
event loop rather than functions, then do it in the event loop.
http://www.PySimpleGUI.org
Copyright 2021, 2022, 2023 PySimpleGUI
Copyright 2021 PySimpleGUI
"""
import PySimpleGUI as sg
@ -77,7 +76,7 @@ def main():
[sg.Text('Status:'), sg.Text(size=(3, 1), key='-STATUS-')],
[sg.Text(size=(50, 1), key='-OUT-')],
[sg.Button('Simple'), sg.Button('Go'), sg.Button('Stop'), sg.Button('Other', key=do_other),
sg.Button('Tuple', key=(1,2)), sg.Button('Lambda', key= lambda window: do_other(window)), sg.Button('Bad')]]
sg.Button('Tuple', key=(1,2)), sg.Button('Bad')]]
window = sg.Window('Dispatchers', layout, font='Default 16', keep_on_top=True)

View file

@ -1,7 +1,7 @@
import PySimpleGUI as sg
"""
Demo "Edit Me" (and Version)
Demo "Edit Me"
More and more of these Demos are getting an "Edit me" option added.
@ -12,26 +12,26 @@ import PySimpleGUI as sg
You can add this capability to your program by adding a right click menu to your window and calling the
editor that you set up in the global PySimpleGUI options.
A constant MENU_RIGHT_CLICK_EDITME_VER_EXIT, when set at the right click menu shows a "Version" and "Edit Me" meny item.
You need to do 2 things to make this work:
1. Add a right click menu - requires you to add 1 parameter to your Window creation
2. Add 1 if statement to your event loop.
You will need to have first set up your editor by using the menu in sg.main()
Copyright 2021, 2022, 2023 PySimpleGUI.org
Copyright 2021 PySimpleGUI.org
"""
layout = [[sg.Text('Edit this program by right clicking and choosing "Edit me"')],
[sg.Button('Exit')]]
window = sg.Window('Edit Me Right Click Menu Demo', layout, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)
window = sg.Window('Edit Me Right Click Menu Demo', layout, right_click_menu=[[''], ['Edit Me', 'Exit',]])
while True: # Event Loop
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
sg.execute_editor(__file__)
window.close()

View file

@ -1,36 +1,38 @@
import PySimpleGUI as sg
# import PySimpleGUIWeb as sg
# import PySimpleGUIWx as sg
# import PySimpleGUIQt as sg
'''
Learn how to send emails from PySimpleGUI using the smtplib and email modules
The GUI portion is simple
Copyright 2019 PySimpleGUI.org
Based on a send-email script originally written by by Israel Dryer
(Thank you Israel for figuring out the hard part of the stmp and email module calls!)
Copyright 2019, 2022 PySimpleGUI
'''
# If you are using a mail service that's not gmail, hotmail, live or yahoo:
# then you can enter the smtp server address here so you don't have to keep typing it into the GUI
smtp_host_default = ''
# used for sending the email
import smtplib as smtp
# used to build the email
from email.message import EmailMessage
# create and send email
def send_an_email(from_address, to_address, subject, message_text, user, password, smtp_host, smtp_port):
def send_an_email(from_address, to_address, subject, message_text, user, password):
# SMTP Servers for popular free services... add your own if needed. Format is: address, port
google_smtp_server = 'smtp.gmail.com', 587
microsoft_smtp_server = 'smtp.office365.com', 587
yahoo_smtp_server = 'smtp.mail.yahoo.com', 587 # or port 465
# open the email server connection
if 'gmail' in user:
smtp_host, smtp_port = google_smtp_server
elif 'hotmail' in user or 'live' in user:
smtp_host, smtp_port = microsoft_smtp_server
elif 'yahoo' in user:
smtp_host, smtp_port = yahoo_smtp_server
else:
sg.popup('Username does not contain a supported email provider')
return
server = smtp.SMTP(host=smtp_host, port=smtp_port)
server.starttls()
try:
server.login(user=user, password=password)
except Exception as e:
sg.popup_error('Error authenticaing your email credentials', e, image=sg.EMOJI_BASE64_WEARY)
server.close()
return
server.login(user=user, password=password)
# create the email message headers and set the payload
msg = EmailMessage()
@ -40,15 +42,9 @@ def send_an_email(from_address, to_address, subject, message_text, user, passwor
msg.set_payload(message_text)
# open the email server and send the message
try:
server.send_message(msg)
except Exception as e:
sg.popup_error('Error sending your email', e, image=sg.EMOJI_BASE64_WEARY)
server.close()
return
server.send_message(msg)
server.close()
sg.popup('Email sent successfully!', image=sg.EMOJI_BASE64_HAPPY_JOY)
'''
important notes about using gmail
@ -65,47 +61,33 @@ def send_an_email(from_address, to_address, subject, message_text, user, passwor
'''
def main():
smtp_server_dict = {'gmail.com':'smtp.gmail.com','hotmail.com':'smtp.office365.com', 'live.com': 'smtp.office365.com', 'yahoo.com':'smtp.mail.yahoo.com'}
sg.theme('Dark Blue 3')
layout = [[sg.Text('Send an Email', font='Default 18')],
[sg.T('From:', size=(8,1)), sg.Input(key='-EMAIL FROM-', size=(35,1))],
[sg.T('To:', size=(8,1)), sg.Input(key='-EMAIL TO-', size=(35,1))],
[sg.T('Subject:', size=(8,1)), sg.Input(key='-EMAIL SUBJECT-', size=(35,1))],
[sg.T('Mail login information', font='Default 18')],
[sg.T('User:', size=(8,1)), sg.Input(key='-USER-', size=(35,1), enable_events=True)],
[sg.T('User:', size=(8,1)), sg.Input(key='-USER-', size=(35,1))],
[sg.T('Password:', size=(8,1)), sg.Input(password_char='*', key='-PASSWORD-', size=(35,1))],
[sg.T('SMTP Server Info', font='_ 14')],
[sg.T('SMTP Hostname'), sg.Input(smtp_host_default, s=20, key='-SMTP HOST-'), sg.T('SMTP Port'), sg.In(587, s=4, key='-SMTP PORT-') ],
[sg.Multiline('Type your message here', size=(60,10), key='-EMAIL TEXT-')],
[sg.Button('Send'), sg.Button('Exit')]]
window = sg.Window('Send An Email', layout)
while True:
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
if event == 'Send':
if values['-SMTP HOST-']:
if sg.__name__ != 'PySimpleGUIWeb': # auto close popups not yet supported in PySimpleGUIWeb
sg.popup_quick_message('Sending your message... this will take a moment...', background_color='red')
send_an_email(from_address=values['-EMAIL FROM-'],
to_address=values['-EMAIL TO-'],
subject=values['-EMAIL SUBJECT-'],
message_text=values['-EMAIL TEXT-'],
user=values['-USER-'],
password=values['-PASSWORD-'],
smtp_host=values['-SMTP HOST-'],
smtp_port = values['-SMTP PORT-'])
else:
sg.popup_error('Missing SMTP Hostname... you have to supply a hostname (gmail, hotmail, live, yahoo are autofilled)')
elif event == '-USER-': # as the email sender is typed in, try to fill in the smtp hostname automatically
for service in smtp_server_dict.keys():
if service in values[event].lower():
window['-SMTP HOST-'].update(smtp_server_dict[service])
break
send_an_email(from_address=values['-EMAIL FROM-'],
to_address=values['-EMAIL TO-'],
subject=values['-EMAIL SUBJECT-'],
message_text=values['-EMAIL TEXT-'],
user=values['-USER-'],
password=values['-PASSWORD-'])
window.close()
if __name__ == '__main__':
main()
main()

File diff suppressed because one or more lines are too long

View file

@ -1,60 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Navigating a window's focus using arrow keys
This Demo Program has 2 features of PySimpleGUI in use:
1. Binding the arrow keys
2. Navigating a window's elements using focus
The first step is to bind the left, right and down arrows to an event.
The call to window.bind will cause events to be generated when these keys are pressed
The next step is to add the focus navigation to your event loop.
When the right key is pressed, the focus moves to the element that should get focus next
When the left arrow key is pressed, the focus moves to the previous element
And when the down arrow is pressed the program exits
Copyright 2022 PySimpleGUI
"""
def main():
layout = [ [sg.Text('My Window')],
[sg.Input(key='-IN-')],
[sg.Input(key='-IN2-')],
[sg.Input(key='-IN3-')],
[sg.Input(key='-IN4-')],
[sg.Input(key='-IN5-')],
[sg.Input(key='-IN6-')],
[sg.Input(key='-IN7-')],
[sg.Button('Go'), sg.Button('Exit')]]
window = sg.Window('Window Title', layout, finalize=True)
# Bind the Left, Right and Down arrow keys to events
window.bind('<Right>', '-NEXT-')
window.bind('<Left>', '-PREV-')
window.bind('<Down>', 'Exit')
while True: # Event Loop
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
# Right arrow pressed, so move to the next element that should get focus
if event == '-NEXT-':
next_element = window.find_element_with_focus().get_next_focus()
next_element.set_focus()
# Left arrow pressed, so move to the previous element that should get focus
if event == '-PREV-':
prev_element = window.find_element_with_focus().get_previous_focus()
prev_element.set_focus()
window.close()
if __name__ == '__main__':
main()

View file

@ -1,6 +1,5 @@
import pyglet
import PySimpleGUI as sg
import os
"""
Demo - Using pyglet to get custom fonts into PySimpleGUI
@ -18,11 +17,8 @@ import os
http://scripts.sil.org/OFL
"""
font_file = os.path.join(os.path.dirname(__file__), "OpenFlame.ttf")
pyglet.font.add_file(r".\OpenFlame.ttf")
pyglet.font.add_file(font_file)
# sg.execute_command_subprocess(font_file)
font1 = ("Open Flame", 40) # Note - use the font "face name" not the filename when specifying the font
font2 = ("Courier New", 40)
font3 = ("Helvetica", 40)

View file

@ -1,47 +0,0 @@
import PySimpleGUI as sg
"""
Demo Frame For Screen Captures
This program can be used to help you record videos.
Because it relies on the "transparent color" feature that's only available on Windows, this Demo is only going
to work the indended way on Windows.
Some video recorders that record a portion of the screen do not show you, at all times, what portion of the screen
is being recorded. This can make it difficult for you to stay within the bounds being recorded.
This demo program is meant to help the situation by showing a thin line that is 20 pixels larger than the area
being recorded.
The top edge of the window has the controls. There's an exit button, a solid "bar" for you to grab with your mouse to move
the frame around your window, and 2 inputs with a "resize" button that enables you to set the frame to the size you want to stay
within.
Copyright 2022 PySimpleGUI.org
"""
def main():
offset = (20, 20) # Number of extra pixels to add to the recording area
default_size = (1920, 1080) # The default size of the recording
location = (None, None) # A specific location to place the window if you want a specific spot
window = sg.Window('Window Title',
[[sg.Button('Exit'), sg.T(sg.SYMBOL_SQUARE * 10, grab=True), sg.I(default_size[0], s=4, k='-W-'), sg.I(default_size[1], s=4, k='-H-'), sg.B('Resize')],
[sg.Frame('', [[]], s=(default_size[0] + offset[0], default_size[1] + offset[1]), k='-FRAME-')]], transparent_color=sg.theme_background_color(),
right_click_menu=['', ['Edit Me', 'Exit']], location=location, no_titlebar=True, keep_on_top=True)
while True: # Event Loop
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Resize':
window['-FRAME-'].set_size((int(values['-W-']) + offset[0], int(values['-H-']) + offset[1]))
window.close()
if __name__ == '__main__':
main()

View file

@ -1,44 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Drag a rectangle and move
This demo shows how to use a Graph Element to draw a square and move it with the mouse.
It's a very simple, single element program. Like many Demo Programs, it started as
a "Test Harness" that demonstrated a bug that happened with a timeout of 0
was added to the window.read()
Original code comes courtesy of user @davesmivers .... Thanks Dave!!
Copyright 2022 PySimpleGUI
"""
GRAPH_SIZE = (400, 400)
START = (200, 200) # We'll assume X and Y are both this value
SQ_SIZE = 40 # Both width and height will be this value
layout = [[sg.Graph(
canvas_size=GRAPH_SIZE, graph_bottom_left=(0, 0), graph_top_right=GRAPH_SIZE, # Define the graph area
change_submits=True, # mouse click events
drag_submits=True, # mouse move events
background_color='lightblue',
key="-GRAPH-",
pad=0)]]
window = sg.Window("Simple Square Movement", layout, finalize=True, margins=(0,0))
# draw the square we'll move around
square = window["-GRAPH-"].draw_rectangle(START, (START[0]+SQ_SIZE, START[1]+SQ_SIZE), fill_color='black')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
print(event, values) if event != sg.TIMEOUT_EVENT else None # our normal debug print, but for this demo, don't spam output with timeouts
if event == "-GRAPH-": # if there's a "Graph" event, then it's a mouse movement. Move the square
x, y = values["-GRAPH-"] # get mouse position
window["-GRAPH-"].relocate_figure(square, x - SQ_SIZE // 2, y + SQ_SIZE // 2) # Move using center of square to mouse pos
window.close()

View file

@ -1,4 +1,5 @@
import PySimpleGUI as sg
from PIL import ImageGrab
"""
Demo - Drawing and moving demo
@ -6,9 +7,22 @@ import PySimpleGUI as sg
This demo shows how to use a Graph Element to (optionally) display an image and then use the
mouse to "drag" and draw rectangles and circles.
Copyright 2020, 2021, 2022, 2023 PySimpleGUI.org
Copyright 2020 PySimpleGUI.org
"""
def save_element_as_file(element, filename):
"""
Saves any element as an image file. Element needs to have an underlyiong Widget available (almost if not all of them do)
:param element: The element to save
:param filename: The filename to save to. The extension of the filename determines the format (jpg, png, gif, ?)
"""
widget = element.Widget
box = (widget.winfo_rootx(), widget.winfo_rooty(), widget.winfo_rootx() + widget.winfo_width(), widget.winfo_rooty() + widget.winfo_height())
grab = ImageGrab.grab(bbox=box)
grab.save(filename)
def main():
sg.theme('Dark Blue 3')
@ -24,6 +38,7 @@ def main():
[sg.R('Bring to front', 1, key='-FRONT-', enable_events=True)],
[sg.R('Move Everything', 1, key='-MOVEALL-', enable_events=True)],
[sg.R('Move Stuff', 1, key='-MOVE-', enable_events=True)],
[sg.B('Save Image', key='-SAVE-')],
]
layout = [[sg.Graph(
@ -34,19 +49,20 @@ def main():
enable_events=True,
background_color='lightblue',
drag_submits=True,
motion_events=True,
right_click_menu=[[''],['Erase item','Send to back']]
right_click_menu=[[],['Erase item',]]
), sg.Col(col, key='-COL-') ],
[sg.Text(key='-INFO-', size=(60, 1))]]
window = sg.Window("Drawing and Moving Stuff Around", layout, finalize=True)
# get the graph element for ease of use later
graph = window["-GRAPH-"] # type: sg.Graph
graph = window["-GRAPH-"] # type: sg.Graph
graph.draw_image(data=logo200, location=(0,400))
dragging = False
start_point = end_point = prior_rect = None
crosshair_lines = []
# graph.bind('<Button-3>', '+RIGHT+')
while True:
event, values = window.read()
print(event, values)
@ -57,14 +73,7 @@ def main():
graph.set_cursor(cursor='fleur') # not yet released method... coming soon!
elif not event.startswith('-GRAPH-'):
graph.set_cursor(cursor='left_ptr') # not yet released method... coming soon!
if event.endswith('+MOVE'):
window["-INFO-"].update(value=f"mouse {values['-GRAPH-']}")
# Delete crosshairs if any exists
if len(crosshair_lines):
for fig in crosshair_lines:
graph.delete_figure(fig)
crosshair_lines = []
window.refresh()
if event == "-GRAPH-": # if there's a "Graph" event, then it's a mouse
x, y = values["-GRAPH-"]
if not dragging:
@ -105,28 +114,25 @@ def main():
for fig in drag_figures:
graph.send_figure_to_back(fig)
window["-INFO-"].update(value=f"mouse {values['-GRAPH-']}")
elif event.endswith('+UP'): # The drawing has ended because mouse up
elif event.endswith('+UP'): # The drawing has ended because mouse up
window["-INFO-"].update(value=f"grabbed rectangle from {start_point} to {end_point}")
start_point, end_point = None, None # enable grabbing a new rect
dragging = False
prior_rect = None
# elif event.endswith('+RIGHT+'): # Right click
# window["-INFO-"].update(value=f"Right clicked location {values['-GRAPH-']}")
# elif event.endswith('+MOTION+'): # Right click
# window["-INFO-"].update(value=f"mouse freely moving {values['-GRAPH-']}")
elif event == 'Send to back': # Right clicked menu item
figures = graph.get_figures_at_location(values["-GRAPH-"]) # get items in front-to-back order
if figures: # make sure at least 1 item found
graph.send_figure_to_back(figures[-1]) # get the last item which will be the top-most
elif event.endswith('+RIGHT+'): # Righ click
window["-INFO-"].update(value=f"Right clicked location {values['-GRAPH-']}")
elif event.endswith('+MOTION+'): # Righ click
window["-INFO-"].update(value=f"mouse freely moving {values['-GRAPH-']}")
elif event == '-SAVE-':
# filename = sg.popup_get_file('Choose file (PNG, JPG, GIF) to save to', save_as=True)
filename=r'test.jpg'
save_element_as_file(window['-GRAPH-'], filename)
elif event == 'Erase item':
window["-INFO-"].update(value=f"Right click erase at {values['-GRAPH-']}")
if values['-GRAPH-'] != (None, None):
figures = graph.get_figures_at_location(values['-GRAPH-'])
if figures:
graph.delete_figure(figures[-1]) # delete the one on top
location = values['-GRAPH-']
crosshair_lines = [graph.draw_line((location[0], 0), (location[0], 800), color='red'),
graph.draw_line((0, location[1]), (800, location[1]), color='red')]
drag_figures = graph.get_figures_at_location(values['-GRAPH-'])
for figure in drag_figures:
graph.delete_figure(figure)
window.close()

File diff suppressed because one or more lines are too long

View file

@ -44,7 +44,7 @@ def convert_to_bytes(file_or_bytes, resize=None):
if resize:
new_width, new_height = resize
scale = min(new_height/cur_height, new_width/cur_width)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.LANCZOS)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.ANTIALIAS)
bio = io.BytesIO()
img.save(bio, format="PNG")
del img

View file

@ -1,30 +1,11 @@
import PySimpleGUI as sg
import math
"""
Demo - Graph Element used to plot a mathematical formula
The Graph element has a flexible coordinate system that you define.
Thie makes is possible for you to work in your coordinates instead of an
arbitrary system.
For example, in a typical mathematics graph, (0,0) is located at the center
of the graph / page / diagram.
This Demo Program shows a graph with (0,0) being at the center of the Graph
area rather than at one of the corners.
It graphs the formula:
y = sine(x/x2) * x1
The values of x1 and x2 can be changed using 2 sliders
Copyright 2018, 2019, 2020, 2021, 2022 PySimpleGUI
"""
# Yet another usage of Graph element.
SIZE_X = 200
SIZE_Y = 200
NUMBER_MARKER_FREQUENCY = SIZE_X//8 # How often to put tick marks on the axis
SIZE_Y = 100
NUMBER_MARKER_FREQUENCY = 25
def draw_axis():
@ -32,45 +13,44 @@ def draw_axis():
graph.draw_line((0, -SIZE_Y), (0, SIZE_Y))
for x in range(-SIZE_X, SIZE_X+1, NUMBER_MARKER_FREQUENCY):
graph.draw_line((x, -SIZE_Y/66), (x, SIZE_Y/66)) # tick marks
graph.draw_line((x, -3), (x, 3)) # tick marks
if x != 0:
# numeric labels
graph.draw_text(str(x), (x, -SIZE_Y/15), color='green', font='courier 10')
graph.draw_text(str(x), (x, -10), color='green')
for y in range(-SIZE_Y, SIZE_Y+1, NUMBER_MARKER_FREQUENCY):
graph.draw_line((-SIZE_X/66, y), (SIZE_X/66, y))
graph.draw_line((-3, y), (3, y))
if y != 0:
graph.draw_text(str(y), (-SIZE_X/11, y), color='blue', font='courier 10')
graph.draw_text(str(y), (-10, y), color='blue')
# Create the graph that will be put into the window. Making outside of layout so have element in a variable
graph = sg.Graph(canvas_size=(500, 500),
sg.theme('DarkAmber')
# Create the graph that will be put into the window
graph = sg.Graph(canvas_size=(400, 400),
graph_bottom_left=(-(SIZE_X+5), -(SIZE_Y+5)),
graph_top_right=(SIZE_X+5, SIZE_Y+5),
background_color='white', expand_x=True, expand_y=True,
key='-GRAPH-')
background_color='white',
key='graph')
# Window layout
layout = [[sg.Text('Graph Element Combined with Math!', justification='center', relief=sg.RELIEF_SUNKEN, expand_x=True, font='Courier 18')],
layout = [[sg.Text('Example of Using Math with a Graph', justification='center', size=(50, 1), relief=sg.RELIEF_SUNKEN)],
[graph],
[sg.Text('y = sin(x / x2) * x1', font='COURIER 18')],
[sg.Text('x1', font='Courier 14'), sg.Slider((0, SIZE_Y), orientation='h', enable_events=True, key='-SLIDER-', expand_x=True)],
[sg.Text('x2', font='Courier 14'), sg.Slider((1, SIZE_Y), orientation='h', enable_events=True, key='-SLIDER2-', expand_x=True)]]
[sg.Text('y = sin(x / x2 * x1)', font='COURIER 18')],
[sg.Text('x1'), sg.Slider((0, 200), orientation='h',
enable_events=True, key='-SLIDER-')],
[sg.Text('x2'), sg.Slider((1, 200), orientation='h', enable_events=True, key='-SLIDER2-')]]
window = sg.Window('Graph of Sine Function', layout, finalize=True)
draw_axis() # draw the axis (an empty graph)
window = sg.Window('Graph of Sine Function', layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
graph.erase() # erase entire graph every time there's a change to a slider
draw_axis() # redraw the axis
# plot the function by drawing short line segments
graph.erase()
draw_axis()
prev_x = prev_y = None
for x in range(-SIZE_X, SIZE_X):
y = math.sin(x/int(values['-SLIDER2-'])) * int(values['-SLIDER-'])
y = math.sin(x/int(values['-SLIDER2-']))*int(values['-SLIDER-'])
if prev_x is not None:
graph.draw_line((prev_x, prev_y), (x, y), color='red')
prev_x, prev_y = x, y

View file

@ -1,45 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Graph Element Rescale Figures When Window Resizes
This demo shows how you can redraw your Graph element's figures so that when
you resize the window, all of the figures on the graph resize.
There may be a tkinter method to help do this?
Copyright 2022 PySimpleGUI
"""
gsize = (400,400)
layout = [ [sg.Text('Rescaling a Graph Element When Window is Resized')],
[sg.Graph(gsize, (0,0),gsize, expand_x=True, expand_y=True, k='-G-', background_color='green')],
[sg.Button('Exit'), sg.Sizegrip()] ]
window = sg.Window('Graph Element Scale With Window', layout, finalize=True, resizable=True, enable_window_config_events=True)
graph = window['-G-'] #type: sg.Graph
orig_win_size = window.current_size_accurate()
# Draw the figure desired (will repeat this code later)
fig = window['-G-'].draw_circle((200, 200), 50, fill_color='blue')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == sg.WINDOW_CONFIG_EVENT: # if get a window resized event
# Determine how much the window was resized by and tell the Graph element the new size for the Canvas
new_size = window.current_size_accurate()
dx = orig_win_size[0]-new_size[0]
dy = orig_win_size[1]-new_size[1]
gsize = (gsize[0] - dx, gsize[1] - dy)
orig_win_size = new_size
graph.CanvasSize = gsize
# Erase entire Graph and redraw all figures0
graph.erase()
# Redraw your figures here
fig = window['-G-'].draw_circle((200, 200), 50, fill_color='blue')
window.close()

View file

@ -45,7 +45,7 @@ def convert_to_bytes(file_or_bytes, resize=None):
if resize:
new_width, new_height = resize
scale = min(new_height/cur_height, new_width/cur_width)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.LANCZOS)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.ANTIALIAS)
with io.BytesIO() as bio:
img.save(bio, format="PNG")
del img

View file

@ -1,32 +0,0 @@
import PySimpleGUI as sg
import urllib.request
"""
Display an Image Located at a URL
Downloads and displays a PNG (or GIF) image given a URL
NOTE:
Early versions of tkinter (for example 8.6.6 found in Python 3.6) have trouble with some PNG formats.
Moving to Python 3.7 fixes this or you can use a tool to re-encode the image (e.g. psgresizer) save it and
it will then work OK in Python 3.6.
Example of one of these images - https://www.python.org/static/community_logos/python-logo-master-v3-TM.png
Copyright 2022 PySimpleGUI.org
"""
image_URL = r'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
layout = [[sg.Image(urllib.request.urlopen(image_URL).read())]]
window = sg.Window('Image From URL', layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
window.close()

View file

@ -1,5 +1,6 @@
import PySimpleGUI as sg
from PIL import Image
import PIL
import os
import base64
import io
@ -24,19 +25,12 @@ import webbrowser
Copyright 2021 PySimpleGUI
"""
version = '1.6.0'
version = '1.3.1'
__version__ = version.split()[0]
'''
Change log
1.6.0 12-July-2022
Fixed output filename to match the size indicated under the filename.
1.5.4 10-May-2022
Had to mess around with the entry point due to setuptools
1.5.0 10-May-2022
Moved icon to bottom of file and called set_global_icon so all windows in this application (including popups) will use this icon
1.4.0 16-Nov-2021
Explicitly set the settings filename. I'm still learning about these PyPI .EXE releases. Need to be explicit rather than default
1.3.1 16-Nov-2021
Added correct readme to PyPI
1.3.0 16-Nov-2021
@ -55,7 +49,7 @@ def resize(input_file, size, output_file=None, encode_format='PNG'):
new_width, new_height = size
if new_width != width or new_height != height: # if the requested size is different than original size
scale = min(new_height / height, new_width / width)
resized_image = image.resize((int(width * scale), int(height * scale)), Image.LANCZOS)
resized_image = image.resize((int(width * scale), int(height * scale)), Image.ANTIALIAS)
else:
resized_image = image
@ -71,6 +65,7 @@ def resize(input_file, size, output_file=None, encode_format='PNG'):
def main():
image_resize_icon = b'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'
def update_outfilename():
infile = values['-IN-']
@ -81,12 +76,8 @@ def main():
window['-ORIG WIDTH-'].update(image.size[0])
if not values['-WIDTH-']:
window['-WIDTH-'].update(image.size[0])
else:
width = values['-WIDTH-']
if not values['-HEIGHT-']:
window['-HEIGHT-'].update(image.size[1])
else:
height = values['-HEIGHT-']
window['-ORIG HEIGHT-'].update(image.size[1])
infilename = os.path.basename(infile)
@ -97,7 +88,7 @@ def main():
outfileext = 'jpg'
else:
outfileext = infileext[1:] # strip off the .
outfile = f'{infilenameonly}_{width}x{height}.{outfileext}'
outfile = f'{infilenameonly}{width}x{height}.{outfileext}'
outfullfilename = os.path.join(os.path.dirname(infile), outfile)
if values['-DO NOT SAVE-']:
@ -113,9 +104,6 @@ def main():
# window['-HEIGHT-'].update('')
window['-NEW FILENAME-'].update()
sg.user_settings_filename(filename='psgresizer.json')
format_list = ('', 'PNG', 'JPEG', 'BMP', 'ICO', 'GIF', 'TIFF')
new_format_layout = [
[sg.Combo(format_list, default_value=sg.user_settings_get_entry('-new format-', ''), readonly=True, enable_events=True, key='-NEW FORMAT-')]]
@ -124,8 +112,8 @@ def main():
[sg.Frame('Input Filename', [[sg.Input(key='-IN-', enable_events=True, s=80), sg.FileBrowse(), ],
[sg.T('Original size'), sg.T(k='-ORIG WIDTH-'), sg.T('X'), sg.T(k='-ORIG HEIGHT-')]])],
[sg.Frame('Output Filename', [[sg.In(k='-NEW FILENAME-', s=80), sg.FileBrowse(), ],
[sg.In(default_text=sg.user_settings_get_entry('-width-', ''), s=4, k='-WIDTH-', enable_events=True), sg.T('X'),
sg.In(default_text=sg.user_settings_get_entry('-height-', ''), s=4, k='-HEIGHT-', enable_events=True)]])],
[sg.In(default_text=sg.user_settings_get_entry('-width-', ''), s=4, k='-WIDTH-'), sg.T('X'),
sg.In(default_text=sg.user_settings_get_entry('-height-', ''), s=4, k='-HEIGHT-')]])],
[sg.Frame('Convert To New Format', new_format_layout)],
[sg.CBox('Encode to Base64 and leave on Clipboard', k='-BASE64-', default=sg.user_settings_get_entry('-base64-', True))],
# [sg.CBox('Use PNG for all Base64 Encoding', default=True, k='-PNG CONVERT-')],
@ -140,7 +128,7 @@ def main():
sg.T('A PySimpleGUI Application - Go to PySimpleGUI home', font='_ 8', enable_events=True, k='-PYSIMPLEGUI-')],
]
window = sg.Window('Resize Image', layout, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_LOC_EXIT,
window = sg.Window('Resize Image', layout, icon=image_resize_icon, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_LOC_EXIT,
enable_close_attempted_event=True, finalize=True)
window['-PSGRESIZER-'].set_cursor('hand1')
window['-PYSIMPLEGUI-'].set_cursor('hand1')
@ -194,8 +182,6 @@ def main():
webbrowser.open_new_tab(r'http://www.PySimpleGUI.com')
elif event == '-PSGRESIZER-':
webbrowser.open_new_tab(r'https://github.com/PySimpleGUI/psgresizer')
elif event in ('-WIDTH-', '-HEIGHT-'):
update_outfilename()
if event != sg.WIN_CLOSED:
sg.user_settings_set_entry('-autoclose-', values['-AUTOCLOSE-'])
@ -207,11 +193,5 @@ def main():
window.close()
def main_entry_point():
image_resize_icon = b'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'
sg.set_global_icon(image_resize_icon)
main()
if __name__ == '__main__':
main_entry_point()
main()

View file

@ -56,7 +56,7 @@ def convert_to_bytes(file_or_bytes, resize=None, fill=False):
if resize:
new_width, new_height = resize
scale = min(new_height / cur_height, new_width / cur_width)
img = img.resize((int(cur_width * scale), int(cur_height * scale)), PIL.Image.LANCZOS)
img = img.resize((int(cur_width * scale), int(cur_height * scale)), PIL.Image.ANTIALIAS)
if fill:
img = make_square(img, THUMBNAIL_SIZE[0])
with io.BytesIO() as bio:

View file

@ -1,31 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Save previously entered value in Input element by using user_settings calls
Tired of typing in the same value or entering the same filename into an Input element?
If so, this may be exactly what you need.
It simply saves the last value you entered so that the next time you start your program, that will be the default
Copyright 2022 PySimpleGUI.org
"""
def main():
sg.user_settings_filename(path='.') # The settings file will be in the same folder as this program
layout = [[sg.T('This is your layout')],
[sg.T('Remembers last value for this:'), sg.In(sg.user_settings_get_entry('-input-', ''), k='-INPUT-')],
[sg.OK(), sg.Button('Exit')]]
# make a window, read it, and automatically close after 1 event happens (button or X to close window)
event, values = sg.Window('Save Input Element Last Value', layout).read(close=True)
# only save the value if OK was clicked
if event == 'OK':
sg.user_settings_set_entry('-input-', values['-INPUT-'])
if __name__ == '__main__':
main()

View file

@ -19,12 +19,13 @@ import PySimpleGUI as sg
For other ports of PySimpleGUI such as the Qt port, the position is remembered by Qt and as a
result this technique using "pin" is not needed.
Copyright 2020, 2022 PySimpleGUI.org
Copyright 2020 PySimpleGUI.org
"""
layout = [ [sg.Text('Hide Button or Multiline. Buttons 1 & 2 hide Button 2')],
[sg.pin(sg.Multiline(size=(60, 10), key='-MLINE-'))],
[sg.pin(sg.Button('Button1')), sg.pin(sg.Button('Button2'), shrink=False), sg.B('Toggle Multiline')],
layout = [ [sg.Text('Hide Button or Input. Button3 hides Input. Buttons 1 & 2 hide Button 2')],
[sg.pin(sg.Input(key='-IN-'))],
[sg.pin(sg.Button('Button1')), sg.pin(sg.Button('Button2')), sg.B('Button3')],
]
window = sg.Window('Visible / Invisible Element Demo', layout)
@ -39,6 +40,7 @@ while True: # Event Loop
if event in ('Button1', 'Button2'):
window['Button2'].update(visible=toggle)
toggle = not toggle
elif event == 'Toggle Multiline':
window['-MLINE-'].update(visible=not window['-MLINE-'].visible)
if event == 'Button3':
window['-IN-'].update(visible=toggle_in)
toggle_in = not toggle_in
window.close()

File diff suppressed because one or more lines are too long

View file

@ -1,65 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Add and "Delete" Rows from a window
This is cut-down version of the Fed-Ex package tracking demo
The purpose is to show a technique for making windows that grow by clicking an "Add Row" button
Each row can be individually "deleted".
The reason for using the quotes are "deleted" is that the elements are simply hidden. The effect is the same as deleting them.
Copyright 2022 PySimpleGUI
"""
def item_row(item_num):
"""
A "Row" in this case is a Button with an "X", an Input element and a Text element showing the current counter
:param item_num: The number to use in the tuple for each element
:type: int
:return: List
"""
row = [sg.pin(sg.Col([[sg.B(sg.SYMBOL_X, border_width=0, button_color=(sg.theme_text_color(), sg.theme_background_color()), k=('-DEL-', item_num), tooltip='Delete this item'),
sg.In(size=(20,1), k=('-DESC-', item_num)),
sg.T(f'Key number {item_num}', k=('-STATUS-', item_num))]], k=('-ROW-', item_num)))]
return row
def make_window():
layout = [ [sg.Text('Add and "Delete" Rows From a Window', font='_ 15')],
[sg.Col([item_row(0)], k='-TRACKING SECTION-')],
[sg.pin(sg.Text(size=(35,1), font='_ 8', k='-REFRESHED-',))],
[sg.T(sg.SYMBOL_X, enable_events=True, k='Exit', tooltip='Exit Application'), sg.T('', enable_events=True, k='Refresh', tooltip='Save Changes & Refresh'), sg.T('+', enable_events=True, k='Add Item', tooltip='Add Another Item')]]
right_click_menu = [[''], ['Add Item', 'Edit Me', 'Version']]
window = sg.Window('Window Title', layout, right_click_menu=right_click_menu, use_default_focus=False, font='_ 15', metadata=0)
return window
def main():
window = make_window()
while True:
event, values = window.read() # wake every hour
print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Add Item':
window.metadata += 1
window.extend_layout(window['-TRACKING SECTION-'], [item_row(window.metadata)])
elif event == 'Edit Me':
sg.execute_editor(__file__)
elif event == 'Version':
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
elif event[0] == '-DEL-':
window[('-ROW-', event[1])].update(visible=False)
window.close()
if __name__ == '__main__':
main()

Some files were not shown because too many files have changed in this diff Show more