Compare commits

..

10 commits

Author SHA1 Message Date
MikeTheWatchGuy
e1daf1046f
Merge pull request #1432 from deajan/calendar_improvements
Allow calendar button locale, format, and default icon usage
2019-05-30 10:35:01 -04:00
Orsiris de Jong
8ff3c80f23
Fix default PySimpleGUI icon never used in CalendarButton 2019-05-19 14:18:58 +02:00
MikeTheWatchGuy
28eed8751c Overhaul to correctly handle tooltips, buttons, etc. Cleanup in a major way 2019-05-18 15:13:47 -04:00
Orsiris de Jong
f563f01ed8
Fix indentation error while merging PRs 2019-05-17 13:18:30 +02:00
Orsiris de Jong
4d137e19c4
Merge PR #1433 to avoid conflicts as same lines affected 2019-05-17 13:17:24 +02:00
Orsiris de Jong
7aa110b1fe
Multiple improvements on CalendarButton
Allow passing optional format argument (as of datetime.strftime() format given https://docs.python.org/3.6/library/datetime.html?highlight=strftime#strftime-strptime-behavior)
Also use current timestamp when updating calendar instead of 00:00:00
2019-05-17 13:11:55 +02:00
Orsiris de Jong
e35fdd8518
Allow calendar button locale
Fixes half of #977
2019-05-17 12:12:38 +02:00
PySimpleGUI
2780e20262
Merge pull request #1401 from musteresel/fix-graph-images-memleak
Fix memory leak due to Images of Graph
2019-05-15 10:16:46 -04:00
Daniel Jour
78c84b9f8f Fix memory leak due to Images of Graph (PySimpleGui27)
- Graph used a list self.Images to which new images where appended on
   DrawImage.  Neither in DeleteFigure nor in Erase were any elements
   removed from that list.  Thus any added image was kept in memory as
   long as the corresponding Graph was; even if it wasn't used
   anymore.

 - Even though self.Images is not referred to in any other way,
   removing the list completely does not work; the result
   is that no images are drawn on the Graph.

 - The implemented solution uses a dictionary (id -> image) to keep
   only used images in self.Images.
2019-05-13 22:51:23 +02:00
Daniel Jour
bdaf9503ee Fix memory leak due to Images of Graph
- Graph used a list self.Images to which new images where appended on
   DrawImage.  Neither in DeleteFigure nor in Erase were any elements
   removed from that list.  Thus any added image was kept in memory as
   long as the corresponding Graph was; even if it wasn't used
   anymore.

 - Even though self.Images is not referred to in any other way,
   removing the list completely does not work; the result
   is that no images are drawn on the Graph.

 - The implemented solution uses a dictionary (id -> image) to keep
   only used images in self.Images.
2019-05-13 22:32:29 +02:00
1169 changed files with 62611 additions and 163812 deletions

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
custom: ["https://www.paypal.me/pythongui", "https://www.buymeacoffee.com/PySimpleGUI"]
github: PySimpleGUI

View file

@ -1 +0,0 @@
blank_issues_enabled: false

18
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View file

@ -0,0 +1,18 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
### Type of Issues (Enhancement, Error, Bug, Question)
### Operating System
### Python version
### PySimpleGUI Port and Version
### Code or partial code causing the problem

View file

@ -1,96 +0,0 @@
---
name: Issue Form - **Must fill in this form** with every new issue submitted.
about: This form contains the information needed to help you solve your problem
title: "[ Enhancement/Bug/Question] NOTE - you can also call sg.main() or sg.main_open_github_issue() to post an issue"
labels: ''
assignees: ''
---
### Type of Issue (Enhancement, Error, Bug, Question)
----------------------------------------
#### Operating System
#### PySimpleGUI Port (tkinter, Qt, Wx, Web)
----------------------------------------
## Versions
Version information can be obtained by calling `sg.main_get_debug_data()`
Or you can print each version shown in ()
#### Python version (`sg.sys.version`)
#### PySimpleGUI Version (`sg.__version__`)
#### GUI Version (tkinter (`sg.tclversion_detailed`), PySide2, WxPython, Remi)
---------------------
#### Your Experience In Months or Years (optional)
Years Python programming experience
Years Programming experience overall
Have used another Python GUI Framework? (tkinter, Qt, etc) (yes/no is fine)
Anything else you think would be helpful?
---------------------
#### Troubleshooting
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
- [ ] 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
#### Code To Duplicate
A **short** program that isolates and demonstrates the problem (Do not paste your massive program, but instead 10-20 lines that clearly show the problem)
This pre-formatted code block is all set for you to paste in your bit of code:
```python
# Paste your code here
```
#### Screenshot, Sketch, or Drawing
---------------------
### Watcha Makin?
If you care to share something about your project, it would be awesome to hear what you're building.

View file

@ -1,3 +0,0 @@
## Pull Request Instructions
**Pull requests are not currently accepted for the project including the core PySimpleGUI code, the Demo Programs and documentation **

2
.gitignore vendored
View file

@ -1,2 +0,0 @@
readme_creator/psg_gui.py

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 +0,0 @@
## 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.
This project/account does not accept user submitted code nor documentation.
### You Can Still Contribute
#### 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 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.
#### 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.
### 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.

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

13
Chess/readme.md Normal file
View file

@ -0,0 +1,13 @@
# PySimpleGUI-Chess A Chess Game Playback Program
## 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

@ -1,747 +0,0 @@
import os.path
import sys
import mmap, re
import warnings
import PySimpleGUI as sg
__version__ = '1.12.2'
"""
PySimpleGUI Demo Program Browser
Originaly written for PySimpleGUI Demo Programs, but expanded to
be a general purpose tool. Enable Advanced Mode in settings for more fun
Use to filter and search your source code tree.
Then run or edit your files
Filter the list of :
* Search using filename
* Searching within the programs' source code (like grep)
The basic file operations are
* Edit a file in your editor
* Run a file
* Filter file list
* Search in files
* Run a regular expression search on all files
* Display the matching line in a file
Additional operations
* Edit this file in editor
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
Key is short filename
Value is the full filename and path
:return: Dictionary of demo files
:rtype: Dict[str:str]
"""
demo_path = get_demo_path()
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'):
fname_full = os.path.join(dirname, filename)
if filename not in demo_files_dict.keys():
demo_files_dict[filename] = fname_full
else:
# Allow up to 100 dupicated names. After that, give up
for i in range(1, 100):
new_filename = f'{filename}_{i}'
if new_filename not in demo_files_dict:
demo_files_dict[new_filename] = fname_full
break
return demo_files_dict
def get_file_list():
"""
Returns list of filenames of files to display
No path is shown, only the short filename
:return: List of filenames
:rtype: List[str]
"""
return sorted(list(get_file_list_dict().keys()))
def get_demo_path():
"""
Get the top-level folder path
:return: Path to list of files using the user settings for this file. Returns folder of this file if not found
:rtype: str
"""
demo_path = sg.user_settings_get_entry('-demos folder-', os.path.dirname(__file__))
return demo_path
def get_global_editor():
"""
Get the path to the editor based on user settings or on PySimpleGUI's global settings
:return: Path to the editor
:rtype: str
"""
try: # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
global_editor = sg.pysimplegui_user_settings.get('-editor program-')
except:
global_editor = ''
return global_editor
def get_editor():
"""
Get the path to the editor based on user settings or on PySimpleGUI's global settings
:return: Path to the editor
:rtype: str
"""
try: # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
global_editor = sg.pysimplegui_user_settings.get('-editor program-')
except:
global_editor = ''
user_editor = sg.user_settings_get_entry('-editor program-', '')
if user_editor == '':
user_editor = global_editor
return user_editor
def using_local_editor():
user_editor = sg.user_settings_get_entry('-editor program-', None)
return get_editor() == user_editor
def get_explorer():
"""
Get the path to the file explorer program
:return: Path to the file explorer EXE
:rtype: str
"""
try: # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
global_explorer = sg.pysimplegui_user_settings.get('-explorer program-', '')
except:
global_explorer = ''
explorer = sg.user_settings_get_entry('-explorer program-', '')
if explorer == '':
explorer = global_explorer
return explorer
def advanced_mode():
"""
Returns True is advanced GUI should be shown
:return: True if user indicated wants the advanced GUI to be shown (set in the settings window)
:rtype: bool
"""
return sg.user_settings_get_entry('-advanced mode-', True)
def get_theme():
"""
Get the theme to use for the program
Value is in this program's user settings. If none set, then use PySimpleGUI's global default theme
:return: The theme
:rtype: str
"""
# First get the current global theme for PySimpleGUI to use if none has been set for this program
try:
global_theme = sg.theme_global()
except:
global_theme = sg.theme()
# Get theme from user settings for this program. Use global theme if no entry found
user_theme = sg.user_settings_get_entry('-theme-', '')
if user_theme == '':
user_theme = global_theme
return user_theme
# We handle our code properly. But in case the user types in a flag, the flags are now in the middle of a regex. Ignore this warning.
warnings.filterwarnings("ignore", category=DeprecationWarning)
# New function
def get_line_number(file_path, string, dupe_lines):
lmn = 0
with open(file_path, encoding="utf-8") as f:
for num, line in enumerate(f, 1):
if string.strip() == line.strip() and num not in dupe_lines:
lmn = num
return lmn
def kill_ascii(s):
return "".join([x if ord(x) < 128 else '?' for x in s])
def find_in_file(string, demo_files_dict, regex=False, verbose=False, window=None, ignore_case=True, show_first_match=True):
"""
Search through the demo files for a string.
The case of the string and the file contents are ignored
:param string: String to search for
:param verbose: if True print the FIRST match
:type verbose: bool
:param find_all_matches: if True, then return all matches in the dictionary
:type find_all_matches: bool
:return: List of files containing the string
:rtype: List[str]
"""
# So you face a predicament here. You wish to read files, both small and large; however the bigger the file/bigger the list, the longer to read the file.
# This probably isn't what you want, right?
# Well, we can't use a direct command line to run grep and parse. But it is an option. The user may not have it.
# We could check if grep exists and if not use our method; but it isn't the best way.
# So using background knowldge, we know that grep is *very* fast.
#
# Why?
# Grep reads a *ton* of files into memory then searches through the memory to find the string or regex/pattern corresponding to the file.
# (This is useful if you ever accidently delete a file, grep may be able to get you the contents of it again!)
# How can we load a file into memory on python as fast as grep whilst keeping it universal?
# memory mapping (mmap).
# We can't load a lot of files into memory as we may face issues with watchdog on other operating systems. So we load one file at a time and search though there.
# This will allow the fastest searching and loading of a file without sacrificing read times.
# 2.8 seconds on the highend for both small and large files in memory.
# We also don't have to iterate over lines this way.
file_list = []
num_files = 0
matched_dict = {}
for file in demo_files_dict:
try:
full_filename = demo_files_dict[file]
if not demo_files_dict == get_file_list_dict():
full_filename = full_filename[0]
matches = None
with open(full_filename, 'rb', 0) as f, mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as s:
if regex:
window['-FIND NUMBER-'].update(f'{num_files} files')
window.refresh()
matches = re.finditer(bytes("^.*(" + string + ").*$", 'utf-8'), s, re.MULTILINE)
if matches:
for match in matches:
if match is not None:
if file not in file_list:
file_list.append(file)
num_files += 1
if verbose:
sg.cprint(f"{file}:", c = 'white on green')
sg.cprint(f"{match.group(0).decode('utf-8')}\n")
else:
window['-FIND NUMBER-'].update(f'{num_files} files')
window.refresh()
matches = None
if ignore_case:
if show_first_match:
matches = re.search(br'(?i)^' + bytes(".*("+re.escape(string.lower()) + ").*$", 'utf-8'), s, re.MULTILINE)
else:
matches = re.finditer(br'(?i)^' + bytes(".*("+re.escape(string.lower()) + ").*$", 'utf-8'), s, re.MULTILINE)
else:
if show_first_match:
matches = re.search(br'^' + bytes(".*("+re.escape(string) + ").*$", 'utf-8'), s, re.MULTILINE)
else:
matches = re.finditer(br'^' + bytes(".*("+re.escape(string) + ").*$", 'utf-8'), s, re.MULTILINE)
if matches:
if show_first_match:
#file_list.append(file)
#num_files += 1
match_array = []
matched_str = matches.group(0).decode('utf-8')
if not all(x in matched_str for x in ("b'", '=')) and len(matched_str) < 500:
# safe to assume this is not a base64 string as it does not contain the proper ending
match_array.append(matches.group(0).decode('utf-8'))
matched_dict[full_filename] = match_array
file_list.append(file)
num_files += 1
else:
# We need to do this because strings are "falsy" in Python, but empty matches still return True...
append_file = False
match_array = []
for match_ in matches:
matched_str = match_.group(0).decode('utf-8')
if matched_str:
if not all(x in matched_str for x in ("b'", '=')) and len(matched_str) < 500:
# if len(match_str) < 500 and "=" not in match_str and "b'" not in match_str:
match_array.append(matched_str)
append_file = True
if append_file:
file_list.append(file)
num_files += 1
matched_dict[full_filename] = match_array
# del matches
except ValueError:
del matches
except Exception as e:
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print(exc_type, fname, exc_tb.tb_lineno)
print(f'{file}', e, file=sys.stderr)
# Format of the matches dictionary
# Filename, [num1, num2, num3]
file_lines_dict = {}
list_of_matches = []
if not regex:
for key in matched_dict:
head, tail = os.path.split(key)
# Tails. Don't wanna put Washington in places he doesn't want to be.
file_array_old = [key]
file_array_new = []
file_match_list = []
if verbose:
sg.cprint(f"{tail}:", c='white on green')
try:
dupe_lines = []
for _match in matched_dict[key]:
line_num_match = get_line_number(key, _match, dupe_lines)
dupe_lines.append(line_num_match)
file_array_new.append(line_num_match)
file_match_list.append(_match) # I *really* overthinked this.
if verbose:
sg.cprint(f"Line: {line_num_match} ", c='white on purple', end='')
sg.cprint(f"{_match.strip()}\n")
# Make a list of the matches found in this file to add to the dictionry
list_of_matches.append(_match.strip())
file_array_old.append(file_array_new)
file_array_old.append(file_match_list)
if tail in file_lines_dict:
for i in range(1, 100):
new_tail = f'{tail}_{i}'
if new_tail not in file_lines_dict:
file_lines_dict[new_tail] = file_array_old
break
else:
file_lines_dict[tail] = file_array_old
except Exception as e:
pass
find_in_file.file_list_dict = file_lines_dict
file_list = list(set(file_list))
return file_list
def window_choose_line_to_edit(filename, full_filename, line_num_list, match_list):
# sg.popup('matches previously found for this file:', filename, line_num_list)
i = 0
if len(line_num_list) == 1:
return full_filename, line_num_list[0]
layout = [[sg.T(f'Choose line from {filename}', font='_ 14')]]
for line in sorted(set(line_num_list)):
match_text = match_list[i]
layout += [[sg.Text(f'Line {line} : {match_text}', key=('-T-', line), enable_events=True, size=(min(len(match_text), 90), None))]]
i += 1
layout += [[sg.B('Cancel')]]
window = sg.Window('Open Editor', layout)
line_chosen = line_num_list[0]
while True:
event, values = window.read()
if event in ('Cancel', sg.WIN_CLOSED):
line_chosen = None
break
# At this point we know a line was chosen
line_chosen = event[1]
break
window.close()
return full_filename, line_chosen
def settings_window():
"""
Show the settings window.
This is where the folder paths and program paths are set.
Returns True if settings were changed
:return: True if settings were changed
:rtype: (bool)
"""
try:
global_editor = sg.pysimplegui_user_settings.get('-editor program-')
except:
global_editor = ''
try:
global_explorer = sg.pysimplegui_user_settings.get('-explorer program-')
except:
global_explorer = ''
try: # in case running with old version of PySimpleGUI that doesn't have a global PSG settings path
global_theme = sg.theme_global()
except:
global_theme = ''
layout = [[sg.T('Program Settings', font='DEFAULT 25')],
[sg.T('Path to Tree', font='_ 16')],
[sg.Combo(sorted(sg.user_settings_get_entry('-folder names-', [])), default_value=sg.user_settings_get_entry('-demos folder-', get_demo_path()), size=(50, 1), key='-FOLDERNAME-'),
sg.FolderBrowse('Folder Browse', target='-FOLDERNAME-'), sg.B('Clear History')],
[sg.T('Editor Program', font='_ 16')],
[sg.T('Leave blank to use global default'), sg.T(global_editor)],
[ sg.In(sg.user_settings_get_entry('-editor program-', ''),k='-EDITOR PROGRAM-'), sg.FileBrowse()],
[sg.T('File Explorer Program', font='_ 16')],
[sg.T('Leave blank to use global default'), sg.T(global_explorer)],
[ sg.In(sg.user_settings_get_entry('-explorer program-'), k='-EXPLORER PROGRAM-'), sg.FileBrowse()],
[sg.T('Theme', font='_ 16')],
[sg.T('Leave blank to use global default'), sg.T(global_theme)],
[sg.Combo(['']+sg.theme_list(),sg.user_settings_get_entry('-theme-', ''), readonly=True, k='-THEME-')],
[sg.T('Double-click a File Will:'), sg.R('Run', 2, sg.user_settings_get_entry('-dclick runs-', False), k='-DCLICK RUNS-'), sg.R('Edit', 2, sg.user_settings_get_entry('-dclick edits-', False), k='-DCLICK EDITS-'), sg.R('Nothing', 2, sg.user_settings_get_entry('-dclick none-', False), k='-DCLICK NONE-')],
[sg.CB('Use Advanced Interface', default=advanced_mode() ,k='-ADVANCED MODE-')],
[sg.B('Ok', bind_return_key=True), sg.B('Cancel')],
]
window = sg.Window('Settings', layout)
settings_changed = False
while True:
event, values = window.read()
if event in ('Cancel', sg.WIN_CLOSED):
break
if event == 'Ok':
sg.user_settings_set_entry('-demos folder-', values['-FOLDERNAME-'])
sg.user_settings_set_entry('-editor program-', values['-EDITOR PROGRAM-'])
sg.user_settings_set_entry('-theme-', values['-THEME-'])
sg.user_settings_set_entry('-folder names-', list(set(sg.user_settings_get_entry('-folder names-', []) + [values['-FOLDERNAME-'], ])))
sg.user_settings_set_entry('-explorer program-', values['-EXPLORER PROGRAM-'])
sg.user_settings_set_entry('-advanced mode-', values['-ADVANCED MODE-'])
sg.user_settings_set_entry('-dclick runs-', values['-DCLICK RUNS-'])
sg.user_settings_set_entry('-dclick edits-', values['-DCLICK EDITS-'])
sg.user_settings_set_entry('-dclick nothing-', values['-DCLICK NONE-'])
settings_changed = True
break
elif event == 'Clear History':
sg.user_settings_set_entry('-folder names-', [])
sg.user_settings_set_entry('-last filename-', '')
window['-FOLDERNAME-'].update(values=[], value='')
window.close()
return settings_changed
ML_KEY = '-ML-' # Multline's key
# --------------------------------- Create the window ---------------------------------
def make_window():
"""
Creates the main window
:return: The main window object
:rtype: (sg.Window)
"""
theme = get_theme()
if not theme:
theme = sg.OFFICIAL_PYSIMPLEGUI_THEME
sg.theme(theme)
# First the window layout...2 columns
find_tooltip = "Find in file\nEnter a string in box to search for string inside of the files.\nFile list will update with list of files string found inside."
filter_tooltip = "Filter files\nEnter a string in box to narrow down the list of files.\nFile list will update with list of files with string in filename."
find_re_tooltip = "Find in file using Regular Expression\nEnter a string in box to search for string inside of the files.\nSearch is performed after clicking the FindRE button."
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.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')],
[sg.Text('Find (F2):', tooltip=find_tooltip), sg.Input(size=(25, 1), enable_events=True, key='-FIND-', tooltip=find_tooltip),
sg.T(size=(15,1), k='-FIND NUMBER-')],
], element_justification='l', expand_x=True, expand_y=True)
lef_col_find_re = sg.pin(sg.Col([
[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.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))],
[sg.T('Python ver ' + sys.version, font='Default 8', pad=(0,0))],
[sg.T('Interpreter ' + sg.execute_py_get_interpreter(), font='Default 8', pad=(0,0))],
]
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-'),
]],
pad=(0,0), k='-OPTIONS BOTTOM-', expand_x=True, expand_y=False), expand_x=True, expand_y=False)
choose_folder_at_top = sg.pin(sg.Column([[sg.T('Click settings to set top of your tree or choose a previously chosen folder'),
sg.Combo(sorted(sg.user_settings_get_entry('-folder names-', [])), default_value=sg.user_settings_get_entry('-demos folder-', ''), size=(50, 30), key='-FOLDERNAME-', enable_events=True, readonly=True)]], pad=(0,0), k='-FOLDER CHOOSE-'))
# ----- Full layout -----
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()]]
# --------------------------------- 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.bind('<F1>', '-FOCUS FILTER-')
window.bind('<F2>', '-FOCUS FIND-')
window.bind('<F3>', '-FOCUS RE FIND-')
if not advanced_mode():
window['-FOLDER CHOOSE-'].update(visible=False)
window['-RE COL-'].update(visible=False)
window['-OPTIONS BOTTOM-'].update(visible=False)
# sg.cprint_set_output_destination(window, ML_KEY)
window.bring_to_front()
return window
# --------------------------------- Main Program Layout ---------------------------------
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):
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',
f'You are currently running version:',
sg.version,
background_color='red', text_color='white')
except Exception as e:
print(f'** Warning Exception parsing version: {version} ** ', f'{e}')
icon = sg.EMOJI_BASE64_HAPPY_IDEA
sg.user_settings_filename('psgdemos.json')
sg.set_options(icon=icon)
find_in_file.file_list_dict = None
old_typed_value = None
file_list_dict = get_file_list_dict()
file_list = get_file_list()
window = make_window()
window['-FILTER NUMBER-'].update(f'{len(file_list)} files')
window.force_focus()
counter = 0
while True:
event, values = window.read()
# print(event, values)
counter += 1
if event in (sg.WINDOW_CLOSED, 'Exit'):
break
if event == '-DEMO LIST-': # if double clicked (used the bind return key parm)
if sg.user_settings_get_entry('-dclick runs-'):
event = 'Run'
elif sg.user_settings_get_entry('-dclick edits-'):
event = 'Edit'
if event == 'Edit':
editor_program = get_editor()
for file in values['-DEMO LIST-']:
if find_in_file.file_list_dict is not None:
full_filename, line = window_choose_line_to_edit(file, find_in_file.file_list_dict[file][0], find_in_file.file_list_dict[file][1], find_in_file.file_list_dict[file][2])
else:
full_filename, line = get_file_list_dict()[file], 1
if line is not None:
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')
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}"')
else:
sg.cprint('Editing canceled')
elif event == 'Run':
sg.cprint('Running....', c='white on green', end='')
sg.cprint('')
for file in values['-DEMO LIST-']:
file_to_run = str(file_list_dict[file])
sg.cprint(file_to_run,text_color='white', background_color='purple')
try:
sp = sg.execute_py_file(file_to_run, pipe_output=values['-WAIT-'])
except Exception as e:
sg.cprint(f'Error trying to run python file. Error info:', e, c='white on red')
try:
if values['-WAIT-']:
sg.cprint(f'Waiting on results..', text_color='white', background_color='red', end='')
while True:
results = sg.execute_get_results(sp)
sg.cprint(f'STDOUT:', text_color='white', background_color='green')
sg.cprint(results[0])
sg.cprint(f'STDERR:', text_color='white', background_color='green')
sg.cprint(results[1])
if not sg.execute_subprocess_still_running(sp):
break
except AttributeError:
sg.cprint('Your version of PySimpleGUI needs to be upgraded to fully use the "WAIT" feature.', c='white on red')
elif event.startswith('Edit Me'):
editor_program = get_editor()
sg.cprint(f'opening using {editor_program}:')
sg.cprint(f'{__file__}', text_color='white', background_color='red', end='')
sg.execute_command_subprocess(f'{editor_program}', f'"{__file__}"')
elif event == '-FILTER-':
new_list = [i for i in file_list if values['-FILTER-'].lower() in i.lower()]
window['-DEMO LIST-'].update(new_list)
window['-FILTER NUMBER-'].update(f'{len(new_list)} files')
window['-FIND NUMBER-'].update('')
window['-FIND-'].update('')
window['-FIND RE-'].update('')
elif event == '-FOCUS FIND-':
window['-FIND-'].set_focus()
elif event == '-FOCUS FILTER-':
window['-FILTER-'].set_focus()
elif event == '-FOCUS RE FIND-':
window['-FIND RE-'].set_focus()
elif event == '-FIND-' or event == '-FIRST MATCH ONLY-' or event == '-VERBOSE-' or event == '-FIND RE-':
is_ignore_case = values['-IGNORE CASE-']
old_ignore_case = False
current_typed_value = str(values['-FIND-'])
if len(values['-FIND-']) == 1:
window[ML_KEY].update('')
window['-VERBOSE-'].update(False)
values['-VERBOSE-'] = False
if values['-VERBOSE-']:
window[ML_KEY].update('')
if values['-FIND-']:
if find_in_file.file_list_dict is None or old_typed_value is None or old_ignore_case is not is_ignore_case:
# New search.
old_typed_value = current_typed_value
file_list = find_in_file(values['-FIND-'], get_file_list_dict(), verbose=values['-VERBOSE-'], window=window, ignore_case=is_ignore_case, show_first_match=values['-FIRST MATCH ONLY-'])
elif current_typed_value.startswith(old_typed_value) and old_ignore_case is is_ignore_case:
old_typed_value = current_typed_value
file_list = find_in_file(values['-FIND-'], find_in_file.file_list_dict, verbose=values['-VERBOSE-'], window=window, ignore_case=is_ignore_case, show_first_match=values['-FIRST MATCH ONLY-'])
else:
old_typed_value = current_typed_value
file_list = find_in_file(values['-FIND-'], get_file_list_dict(), verbose=values['-VERBOSE-'], window=window, ignore_case=is_ignore_case, show_first_match=values['-FIRST MATCH ONLY-'])
window['-DEMO LIST-'].update(sorted(file_list))
window['-FIND NUMBER-'].update(f'{len(file_list)} files')
window['-FILTER NUMBER-'].update('')
window['-FIND RE-'].update('')
window['-FILTER-'].update('')
elif values['-FIND RE-']:
window['-ML-'].update('')
file_list = find_in_file(values['-FIND RE-'], get_file_list_dict(), regex=True, verbose=values['-VERBOSE-'],window=window)
window['-DEMO LIST-'].update(sorted(file_list))
window['-FIND NUMBER-'].update(f'{len(file_list)} files')
window['-FILTER NUMBER-'].update('')
window['-FIND-'].update('')
window['-FILTER-'].update('')
elif event == 'Find RE':
window['-ML-'].update('')
file_list = find_in_file(values['-FIND RE-'], get_file_list_dict(), regex=True, verbose=values['-VERBOSE-'],window=window)
window['-DEMO LIST-'].update(sorted(file_list))
window['-FIND NUMBER-'].update(f'{len(file_list)} files')
window['-FILTER NUMBER-'].update('')
window['-FIND-'].update('')
window['-FILTER-'].update('')
sg.cprint('Regular expression find completed')
elif event == 'Settings':
if settings_window() is True:
window.close()
window = make_window()
file_list_dict = get_file_list_dict()
file_list = get_file_list()
window['-FILTER NUMBER-'].update(f'{len(file_list)} files')
elif event == 'Clear':
file_list = get_file_list()
window['-FILTER-'].update('')
window['-FILTER NUMBER-'].update(f'{len(file_list)} files')
window['-FIND-'].update('')
window['-DEMO LIST-'].update(file_list)
window['-FIND NUMBER-'].update('')
window['-FIND RE-'].update('')
window['-ML-'].update('')
elif event == '-FOLDERNAME-':
sg.user_settings_set_entry('-demos folder-', values['-FOLDERNAME-'])
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('')
elif event == 'Open Folder':
explorer_program = get_explorer()
if explorer_program:
sg.cprint(f'Opening Folder using {explorer_program}...', c='white on green', end='')
sg.cprint('')
for file in values['-DEMO LIST-']:
file_selected = str(file_list_dict[file])
file_path = os.path.dirname(file_selected)
if sg.running_windows():
file_path = file_path.replace('/', '\\')
sg.cprint(file_path, text_color='white', background_color='purple')
sg.execute_command_subprocess(explorer_program, file_path)
elif event == 'Copy Path':
for file in values['-DEMO LIST-']:
sg.cprint('Copying the last highlighted filename in your list')
if find_in_file.file_list_dict is not None:
full_filename, line = window_choose_line_to_edit(file, find_in_file.file_list_dict[file][0], find_in_file.file_list_dict[file][1], find_in_file.file_list_dict[file][2])
else:
full_filename, line = get_file_list_dict()[file], 1
if line is not None:
sg.cprint(f'Added to Clipboard Full Path {full_filename}', c='white on purple')
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()
if __name__ == '__main__':
main()

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View file

@ -1,163 +0,0 @@
#!/usr/bin/env python
"""
Example of (almost) all Elements, that you can use in PySimpleGUI.
Shows you the basics including:
Naming convention for keys
Menubar format
Right click menu format
Table format
Running an async event loop
Theming your application (requires a window restart)
Displays the values dictionary entry for each element
And more!
Copyright 2021, 2022, 2023 PySimpleGUI
"""
import PySimpleGUI as sg
def make_window(theme):
sg.theme(theme)
menu_def = [['&Application', ['E&xit']],
['&Help', ['&About']] ]
right_click_menu_def = [[], ['Edit Me', 'Versions', 'Nothing','More Nothing','Exit']]
graph_right_click_menu_def = [[], ['Erase','Draw Line', 'Draw',['Circle', 'Rectangle', 'Image'], 'Exit']]
# Table Data
data = [["John", 10], ["Jen", 5]]
headings = ["Name", "Score"]
input_layout = [
# [sg.Menu(menu_def, key='-MENU-')],
[sg.Text('Anything that requires user-input is in this tab!')],
[sg.Input(key='-INPUT-')],
[sg.Slider(orientation='h', key='-SKIDER-'),
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.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-')],
[sg.Button('Button'), sg.Button('Popup'), sg.Button(image_data=sg.DEFAULT_BASE64_ICON, key='-LOGO-')]]
asthetic_layout = [[sg.T('Anything that you would use for asthetics is in this tab!')],
[sg.Image(data=sg.DEFAULT_BASE64_ICON, k='-IMAGE-')],
[sg.ProgressBar(100, orientation='h', size=(20, 20), key='-PROGRESS BAR-'), sg.Button('Test Progress bar')]]
logging_layout = [[sg.Text("Anything printed will display here!")],
[sg.Multiline(size=(60,15), font='Courier 8', expand_x=True, expand_y=True, write_only=True,
reroute_stdout=True, reroute_stderr=True, echo_stdout_stderr=True, autoscroll=True, auto_refresh=True)]
# [sg.Output(size=(60,15), font='Courier 8', expand_x=True, expand_y=True)]
]
graphing_layout = [[sg.Text("Anything you would use to graph will display here!")],
[sg.Graph((200,200), (0,0),(200,200),background_color="black", key='-GRAPH-', enable_events=True,
right_click_menu=graph_right_click_menu_def)],
[sg.T('Click anywhere on graph to draw a circle')],
[sg.Table(values=data, headings=headings, max_col_width=25,
background_color='black',
auto_size_columns=True,
display_row_numbers=True,
justification='right',
num_rows=2,
alternating_row_color='black',
key='-TABLE-',
row_height=25)]]
popup_layout = [[sg.Text("Popup Testing")],
[sg.Button("Open Folder")],
[sg.Button("Open File")]]
theme_layout = [[sg.Text("See how elements look under different themes by choosing a different theme here!")],
[sg.Listbox(values = sg.theme_list(),
size =(20, 12),
key ='-THEME LISTBOX-',
enable_events = True)],
[sg.Button("Set Theme")]]
layout = [ [sg.MenubarCustom(menu_def, key='-MENU-', font='Courier 15', tearoff=True)],
[sg.Text('Demo Of (Almost) All Elements', size=(38, 1), justification='center', font=("Helvetica", 16), relief=sg.RELIEF_RIDGE, k='-TEXT HEADING-', enable_events=True)]]
layout +=[[sg.TabGroup([[ sg.Tab('Input Elements', input_layout),
sg.Tab('Asthetic Elements', asthetic_layout),
sg.Tab('Graphing', graphing_layout),
sg.Tab('Popups', popup_layout),
sg.Tab('Theming', theme_layout),
sg.Tab('Output', logging_layout)]], key='-TAB GROUP-', expand_x=True, expand_y=True),
]]
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.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
if event not in (sg.TIMEOUT_EVENT, sg.WIN_CLOSED):
print('============ Event = ', event, ' ==============')
print('-------- Values Dictionary (key=value) --------')
for key in values:
print(key, ' = ',values[key])
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':
print("[LOG] Clicked About!")
sg.popup('PySimpleGUI Demo All Elements',
'Right click anywhere to see right click menu',
'Visit each of the tabs to see available elements',
'Output of event and values can be see in Output tab',
'The event and values dictionary is printed after every event', keep_on_top=True)
elif event == 'Popup':
print("[LOG] Clicked Popup Button!")
sg.popup("You pressed a button!", keep_on_top=True)
print("[LOG] Dismissing Popup!")
elif event == 'Test Progress bar':
print("[LOG] Clicked Test Progress Bar!")
progress_bar = window['-PROGRESS BAR-']
for i in range(100):
print("[LOG] Updating progress bar by 1 step ("+str(i)+")")
progress_bar.update(current_count=i + 1)
print("[LOG] Progress bar complete!")
elif event == "-GRAPH-":
graph = window['-GRAPH-'] # type: sg.Graph
graph.draw_circle(values['-GRAPH-'], fill_color='yellow', radius=20)
print("[LOG] Circle drawn at: " + str(values['-GRAPH-']))
elif event == "Open Folder":
print("[LOG] Clicked Open Folder!")
folder_or_file = sg.popup_get_folder('Choose your folder', keep_on_top=True)
sg.popup("You chose: " + str(folder_or_file), keep_on_top=True)
print("[LOG] User chose folder: " + str(folder_or_file))
elif event == "Open File":
print("[LOG] Clicked Open File!")
folder_or_file = sg.popup_get_file('Choose your file', keep_on_top=True)
sg.popup("You chose: " + str(folder_or_file), keep_on_top=True)
print("[LOG] User chose file: " + str(folder_or_file))
elif event == "Set Theme":
print("[LOG] Clicked Set Theme!")
theme_chosen = values['-THEME LISTBOX-'][0]
print("[LOG] User Chose Theme: " + str(theme_chosen))
window.close()
window = make_window(theme_chosen)
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)
window.close()
exit(0)
if __name__ == '__main__':
sg.theme('black')
sg.theme('dark red')
sg.theme('dark green 7')
# sg.theme('DefaultNoMoreNagging')
main()

View file

@ -1,107 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Element List
All elements shown in 1 window as simply as possible.
Copyright 2022 PySimpleGUI
"""
use_custom_titlebar = True if sg.running_trinket() else 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], )
treedata.Insert("", '_B_', 'B', [])
treedata.Insert("_A_", '_A1_', 'Sub Item 1', ['can', 'be', 'anything'], )
layout_l = [
[name('Text'), sg.Text('Text')],
[name('Input'), sg.Input(s=15)],
[name('Multiline'), sg.Multiline(s=(15,2))],
[name('Output'), sg.Output(s=(15,2))],
[name('Combo'), sg.Combo(sg.theme_list(), default_value=sg.theme(), s=(15,22), enable_events=True, readonly=True, k='-COMBO-')],
[name('OptionMenu'), sg.OptionMenu(['OptionMenu',],s=(15,2))],
[name('Checkbox'), sg.Checkbox('Checkbox')],
[name('Radio'), sg.Radio('Radio', 1)],
[name('Spin'), sg.Spin(['Spin',], s=(15,2))],
[name('Button'), sg.Button('Button')],
[name('ButtonMenu'), sg.ButtonMenu('ButtonMenu', sg.MENU_RIGHT_CLICK_EDITME_EXIT)],
[name('Slider'), sg.Slider((0,10), orientation='h', s=(10,15))],
[name('Listbox'), sg.Listbox(['Listbox', 'Listbox 2'], no_scrollbar=True, s=(15,2))],
[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))],
[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)],
[name('Horizontal Separator'), sg.HSep()],
[name('Vertical Separator'), sg.VSep()],
[name('Frame'), sg.Frame('Frame', [[sg.T(s=15)]])],
[name('Column'), sg.Column([[sg.T(s=15)]])],
[name('Tab, TabGroup'), sg.TabGroup([[sg.Tab('Tab1',[[sg.T(s=(15,2))]]), sg.Tab('Tab2', [[]])]])],
[name('Pane'), sg.Pane([sg.Col([[sg.T('Pane 1')]]), sg.Col([[sg.T('Pane 2')]])])],
[name('Push'), sg.Push(), sg.T('Pushed over')],
[name('VPush'), sg.VPush()],
[name('Sizer'), sg.Sizer(1,1)],
[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)]]
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)
window['-PBAR-'].update(30) # Show 30% complete on ProgressBar
window['-GRAPH-'].draw_image(data=sg.EMOJI_BASE64_HAPPY_JOY, location=(0,50)) # Draw something in the Graph Element
return window
window = make_window()
while True:
event, values = window.read()
# sg.Print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
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)
window.close()

View file

@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
sg.ChangeLookAndFeel('GreenTan')
# ------ Menu Definition ------ #
menu_def = [['&File', ['&Open', '&Save', 'E&xit', 'Properties']],
['&Edit', ['Paste', ['Special', 'Normal', ], 'Undo'], ],
['&Help', '&About...'], ]
# ------ Column Definition ------ #
column1 = [[sg.Text('Column 1', background_color='lightblue', justification='center', size=(10, 1))],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 1')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 2')],
[sg.Spin(values=('Spin Box 1', '2', '3'), initial_value='Spin Box 3')]]
layout = [
[sg.Menu(menu_def, tearoff=True)],
[sg.Text('(Almost) All widgets in one Window!', size=(30, 1), justification='center', font=("Helvetica", 25), relief=sg.RELIEF_RIDGE)],
[sg.Text('Here is some text.... and a place to enter text')],
[sg.InputText('This is my text')],
[sg.Frame(layout=[
[sg.Checkbox('Checkbox', size=(10,1)), sg.Checkbox('My second checkbox!', default=True)],
[sg.Radio('My first Radio! ', "RADIO1", default=True, size=(10,1)), sg.Radio('My second Radio!', "RADIO1")]], title='Options',title_color='red', relief=sg.RELIEF_SUNKEN, tooltip='Use these to set flags')],
[sg.Multiline(default_text='This is the default Text should you decide not to type anything', size=(35, 3)),
sg.Multiline(default_text='A second multi-line', size=(35, 3))],
[sg.InputCombo(('Combobox 1', 'Combobox 2'), size=(20, 1)),
sg.Slider(range=(1, 100), orientation='h', size=(34, 20), default_value=85)],
[sg.InputOptionMenu(('Menu Option 1', 'Menu Option 2', 'Menu Option 3'))],
[sg.Listbox(values=('Listbox 1', 'Listbox 2', 'Listbox 3'), size=(30, 3)),
sg.Frame('Labelled Group',[[
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=25, tick_interval=25),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=75),
sg.Slider(range=(1, 100), orientation='v', size=(5, 20), default_value=10),
sg.Column(column1, background_color='lightblue')]])],
[sg.Text('_' * 80)],
[sg.Text('Choose A Folder', size=(35, 1))],
[sg.Text('Your Folder', size=(15, 1), auto_size_text=False, justification='right'),
sg.InputText('Default Folder'), sg.FolderBrowse()],
[sg.Submit(tooltip='Click to submit this form'), sg.Cancel()]]
window = sg.Window('Everything bagel', default_element_size=(40, 1), grab_anywhere=False).Layout(layout)
event, values = window.Read()
sg.Popup('Title',
'The results of the window.',
'The button clicked was "{}"'.format(event),
'The values are', values)

File diff suppressed because one or more lines are too long

View file

@ -1,33 +0,0 @@
from PIL import Image, ImageTk, ImageSequence
import PySimpleGUI as sg
"""
Demo_Animated_GIFs_Using_PIL.py
You'll find other animated GIF playback demos for PySimpleGUI that use the tkinter built-in GIF parser.
That is how the built-in PySimpleGUI Image.update_animation is used.
If you want to do the GIF file parsing yourself using PIL and update your Image element yourself, then
this is one possible technique.
This particular demo will loop playing the GIF file over and over. To not loop, remove the while True statement.
Copyright 2020 PySimpleGUI.org
"""
gif_filename = r'ExampleGIF.gif'
layout = [[sg.Text('Happy Thursday!', background_color='#A37A3B', text_color='#FFF000', justification='c', key='-T-', font=("Bodoni MT", 40))],
[sg.Image(key='-IMAGE-')]]
window = sg.Window('Window Title', layout, element_justification='c', margins=(0,0), element_padding=(0,0), finalize=True)
window['-T-'].expand(True, True, True) # Make the Text element expand to take up all available space
interframe_duration = Image.open(gif_filename).info['duration'] # get how long to delay between frames
while True:
for frame in ImageSequence.Iterator(Image.open(gif_filename)):
event, values = window.read(timeout=interframe_duration)
if event == sg.WIN_CLOSED:
exit(0)
window['-IMAGE-'].update(data=ImageTk.PhotoImage(frame) )

View file

@ -1,29 +0,0 @@
import PySimpleGUI as sg
"""
Simple template window that saves position.
Rather than starting in the middle of the screen, this code will save the position the window was in when it last exited.
To pull this off it's going to be.... super.....?hard?
No... of course it's going to be... SIMPLE
There is one added line of code. When the user attempts to close the window, that's when the position is saved.
When the program starts, it reads the previously saved position as part of the window creation. User Settings APIs rock!
Copyright 2021 PySimpleGUI
"""
layout = [[sg.Text('Window that Auto-saves position', font='_ 25')],
[sg.Button('Ok'), sg.Button('Exit')]]
window = sg.Window('Auto-saves Location', layout, enable_close_attempted_event=True, location=sg.user_settings_get_entry('-location-', (None, None)))
while True:
event, values = window.read()
print(event, values)
if event in ('Exit', sg.WINDOW_CLOSE_ATTEMPTED_EVENT):
sg.user_settings_set_entry('-location-', window.current_location()) # The line of code to save the position before exiting
break
window.close()

View file

@ -1,55 +1,28 @@
import PySimpleGUI as sg import PySimpleGUI as sg
import random import random
""" BAR_WIDTH = 50
Demo - Using a Graph Element to make Bar Charts BAR_SPACING = 75
EDGE_OFFSET = 3
GRAPH_SIZE = (500,500)
DATA_SIZE = (500,500)
The Graph Element is very versatile. Because you can define your own graph = sg.Graph(GRAPH_SIZE, (0, 0), DATA_SIZE)
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). layout = [[sg.Text('Bar graphs using PySimpleGUI')],
[graph],
[sg.Button('OK')]]
To make things a little more interesting, this is a barchart with that data values window = sg.Window('Window Title').Layout(layout)
placed as labels atop each bar, another Graph element method (draw_text)
Copyright 2022 PySimpleGUI
"""
BAR_WIDTH = 50 # width of each bar
BAR_SPACING = 75 # space between each bar
EDGE_OFFSET = 3 # offset from the left edge for first bar
GRAPH_SIZE= DATA_SIZE = (500,500) # size in pixels
sg.theme('Light brown 1')
layout = [[sg.Text('Labelled Bar graphs using PySimpleGUI')],
[sg.Graph(GRAPH_SIZE, (0,0), DATA_SIZE, k='-GRAPH-')],
[sg.Button('OK'), sg.T('Click to display more data'), sg.Exit()]]
window = sg.Window('Bar Graph', layout, finalize=True)
graph = window['-GRAPH-'] # type: sg.Graph
while True: while True:
event, values = window.Read()
graph.erase() graph.Erase()
for i in range(7): if event is None:
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.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])
graph.draw_text(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10), font='_ 14')
# Normally at the top of the loop, but because we're drawing the graph first, making it at the bottom
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break break
for i in range(7):
window.close() graph_value = random.randint(0, 400)
graph.DrawRectangle(top_left=(i * BAR_SPACING + EDGE_OFFSET, graph_value),
bottom_right=(i * BAR_SPACING + EDGE_OFFSET + BAR_WIDTH, 0), fill_color='blue')
graph.DrawText(text=graph_value, location=(i*BAR_SPACING+EDGE_OFFSET+25, graph_value+10))

View file

@ -1,27 +1,27 @@
# Base64 Encoder - encodes a folder of PNG files and creates a .py file with definitions
import PySimpleGUI as sg import PySimpleGUI as sg
import os import os
import base64 import base64
''' '''
Make base64 images Base64 Encoder - encodes a folder of PNG files and creates a .py file with definitions
input: folder with .png .ico .gif 's
output: output.py file with variables
''' '''
OUTPUT_FILENAME = 'output.py'
def main(): def main():
OUTPUT_FILENAME = 'output.py' # folder = r'C:\Python\PycharmProjects\GooeyGUI\Uno Cards'
folder=''
folder = sg.PopupGetFolder('Source folder for images\nImages will be encoded and results saved to %s'%OUTPUT_FILENAME,
title='Base64 Encoder',
default_path=folder, initial_folder=folder )
folder = sg.popup_get_folder('Source folder for images\nImages will be encoded and results saved to %s'%OUTPUT_FILENAME, if folder is None or folder == '':
title='Base64 Encoder') sg.PopupCancel('Cancelled - No valid folder entered')
if not folder:
sg.popup_cancel('Cancelled - No valid folder entered')
return return
try: try:
namesonly = [f for f in os.listdir(folder) if f.endswith('.png') or f.endswith('.ico') or f.endswith('.gif')] namesonly = [f for f in os.listdir(folder) if f.endswith('.png') or f.endswith('.ico')]
except: except:
sg.popup_cancel('Cancelled - No valid folder entered') sg.PopupCancel('Cancelled - No valid folder entered')
return return
outfile = open(os.path.join(folder, OUTPUT_FILENAME), 'w') outfile = open(os.path.join(folder, OUTPUT_FILENAME), 'w')
@ -29,11 +29,12 @@ def main():
for i, file in enumerate(namesonly): for i, file in enumerate(namesonly):
contents = open(os.path.join(folder, file), 'rb').read() contents = open(os.path.join(folder, file), 'rb').read()
encoded = base64.b64encode(contents) encoded = base64.b64encode(contents)
outfile.write('\n{} = {}'.format(file[:file.index(".")], encoded)) outfile.write('\n{} = {}\n\n'.format(file[:file.index(".")], encoded))
sg.OneLineProgressMeter('Base64 Encoding', i+1, len(namesonly), key='-METER-') sg.OneLineProgressMeter('Base64 Encoding', i+1, len(namesonly),key='_METER_')
outfile.close() outfile.close()
sg.popup('Completed!', 'Encoded %s files'%(i+1)) sg.Popup('Completed!', 'Encoded %s files'%(i+1))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -1,43 +0,0 @@
import PySimpleGUI as sg
import base64
"""
Make base64 image from a file
This is usually done in order to create a Base64 image for use as an Ucon or a Button image
To use, either copy and paste the full path to the file or use the browse button to locate the file.
Once chosen, the conversion will happen automatically with the result placed on the clipboard.
When complete, a popup window is shown that tells you to paste the image before closing the window. This is because of a
tkinter problem on Linux. On Windows you can close the Window, but on Linux, you'll need to keep it open until the paste completes
NOTE - if you're replacing your ICO file for your window with a base64 image, you will first need to convert your icon from
an ICO file into a PNG file. Encode the PNG file and then you'll be able to pass that value in your call to Window:
window = sg.Window('Window Title', layout, icon=icon)
Where icon is a variable you created using the contents of the clipboard folowing running this program.
Input: a single image file
Output: clipboard will contain the Base64 Byte String of the source image
Copyright 2021 PySimpleGUI
"""
def convert_file_to_base64(filename):
try:
contents = open(filename, 'rb').read()
encoded = base64.b64encode(contents)
sg.clipboard_set(encoded)
# pyperclip.copy(str(encoded))
sg.popup('Copied to your clipboard!', 'Keep window open until you have pasted the base64 bytestring')
except Exception as error:
sg.popup_error('Cancelled - An error occurred', error)
if __name__ == '__main__':
filename = sg.popup_get_file('Source Image will be encoded and results placed on clipboard', title='Base64 Encoder')
if filename:
convert_file_to_base64(filename)
else:
sg.popup_cancel('Cancelled - No valid file entered')

View file

@ -1,25 +1,29 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
# Turn off padding in order to get a really tight looking layout. """
Turn off padding in order to get a really tight looking layout.
"""
sg.theme('Dark') sg.ChangeLookAndFeel('Dark')
sg.set_options(element_padding=(0, 0)) sg.SetOptions(element_padding=(0, 0))
layout = [[sg.Text('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)), layout = [[sg.T('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)),
sg.Text('0', size=(8, 1))], sg.T('0', size=(8, 1))],
[sg.Text('Customer:', pad=((3, 0), 0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20, 1)), [sg.T('Customer:', pad=((3, 0), 0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20, 1)),
sg.Text('1', size=(8, 1))], sg.T('1', size=(8, 1))],
[sg.Text('Notes:', pad=((3, 0), 0)), [sg.T('Notes:', pad=((3, 0), 0)), sg.In(size=(44, 1), background_color='white', text_color='black')],
sg.Input(size=(44, 1), background_color='white', text_color='black')], [sg.ReadButton('Start', button_color=('white', 'black')),
[sg.Button('Start', button_color=('white', 'black')), sg.ReadButton('Stop', button_color=('gray50', 'black')),
sg.Button('Stop', button_color=('gray50', 'black')), sg.ReadButton('Reset', button_color=('white', '#9B0023')),
sg.Button('Reset', button_color=('white', '#9B0023')), sg.ReadButton('Submit', button_color=('gray60', 'springgreen4')),
sg.Button('Submit', button_color=('gray60', 'springgreen4')),
sg.Button('Exit', button_color=('white', '#00406B'))]] sg.Button('Exit', button_color=('white', '#00406B'))]]
window = sg.Window("Borderless Window", window = sg.Window("Borderless Window",
layout,
default_element_size=(12, 1), default_element_size=(12, 1),
text_justification='r', text_justification='r',
auto_size_text=False, auto_size_text=False,
@ -28,7 +32,11 @@ window = sg.Window("Borderless Window",
grab_anywhere=True, grab_anywhere=True,
default_button_element_size=(12, 1)) default_button_element_size=(12, 1))
window.Layout(layout)
while True: while True:
event, values = window.read() event, values = window.Read()
if event in (sg.WIN_CLOSED, 'Exit'): if event is None or event == 'Exit':
break break

File diff suppressed because one or more lines are too long

View file

@ -1,27 +1,33 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
import PySimpleGUI as sg if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
if not sys.platform.startswith('win'): if not sys.platform.startswith('win'):
sg.popup_error('Sorry, you gotta be on Windows') sg.PopupError('Sorry, you gotta be on Windows')
sys.exit() sys.exit()
import winsound import winsound
sg.ChangeLookAndFeel('Dark')
sg.SetOptions(element_padding=(0,0))
layout = [ layout = [
[sg.Button('Start', button_color=('white', 'black'), key='start'), [sg.ReadButton('Start', button_color=('white', 'black'), key='start'),
sg.Button('Stop', button_color=('white', 'black'), key='stop'), sg.ReadButton('Stop', button_color=('white', 'black'), key='stop'),
sg.Button('Reset', button_color=('white', 'firebrick3'), key='reset'), sg.ReadButton('Reset', button_color=('white', 'firebrick3'), key='reset'),
sg.Button('Submit', button_color=('white', 'springgreen4'), key='submit')] sg.ReadButton('Submit', button_color=('white', 'springgreen4'), key='submit')]
] ]
window = sg.Window("Button Click", layout, auto_size_buttons=False, default_button_element_size=(12,1), use_default_focus=False, finalize=True) window = sg.Window("Button Click", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False, default_button_element_size=(12,1), use_default_focus=False).Layout(layout).Finalize()
window['submit'].update(disabled=True) window.FindElement('submit').Update(disabled=True)
recording = have_data = False recording = have_data = False
while True: while True:
event, values = window.read(timeout=100) event, values = window.Read()
if event == sg.WINDOW_CLOSED: if event is None:
break sys.exit(69)
winsound.PlaySound("ButtonClick.wav", 1) if event != sg.TIMEOUT_KEY else None winsound.PlaySound("ButtonClick.wav", 1)
window.close()

View file

@ -1,32 +0,0 @@
import PySimpleGUI as sg
"""
Demo - Fill a listbox with list of files FilesBrowse button
This technique can be used to generate events from "Chooser Buttons" like FileBrowse, FilesBrowse
FolderBrowser, ColorChooserButton, Calendar Button
Any button that uses a "Target" can be used with an invisible Input Element to generate an
event when the user has made a choice. Enable events for the invisible element and an event will
be generated when the Chooser Button fills in the element
This particular demo users a list of chosen files to populate a listbox
"""
layout = [ [sg.LBox([], size=(20,10), key='-FILESLB-')],
[sg.Input(visible=False, enable_events=True, key='-IN-'), sg.FilesBrowse()],
[sg.Button('Go'), sg.Button('Exit')] ]
window = sg.Window('Window Title', layout)
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
# When choice has been made, then fill in the listbox with the choices
if event == '-IN-':
window['-FILESLB-'].Update(values['-IN-'].split(';'))
window.close()

View file

@ -1,6 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
import PySimpleGUI as sg if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
""" """
Demo Button Function Calls Demo Button Function Calls
@ -14,29 +18,26 @@ It is quite easy to simulate these callbacks however. The way to do this is to
to your Event Loop to your Event Loop
""" """
def callback_function1(): def callback_function1():
sg.popup('In Callback Function 1') sg.Popup('In Callback Function 1')
print('In the callback function 1') print('In the callback function 1')
def callback_function2(): def callback_function2():
sg.popup('In Callback Function 2') sg.Popup('In Callback Function 2')
print('In the callback function 2') print('In the callback function 2')
layout = [ [sg.Text('Demo of Button Callbacks')],
[sg.Button('Button 1'), sg.Button('Button 2')] ]
layout = [[sg.Text('Demo of Button Callbacks')], window = sg.Window('Button Callback Simulation').Layout(layout)
[sg.Button('Button 1'), sg.Button('Button 2')]]
window = sg.Window('Button Callback Simulation', layout)
while True: # Event Loop while True: # Event Loop
event, values = window.read() event, values = window.Read()
if event == sg.WIN_CLOSED: if event is None:
break break
elif event == 'Button 1': elif event == 'Button 1':
callback_function1() # call the "Callback" function callback_function1() # call the "Callback" function
elif event == 'Button 2': elif event == 'Button 2':
callback_function2() # call the "Callback" function callback_function2() # call the "Callback" function
window.close() window.Close()

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,60 +1,51 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
""" """
Demonstrates using a "tight" layout with a Dark theme. Demonstrates using a "tight" layout with a Dark theme.
Shows how button states can be controlled by a user application. The program manages the disabled/enabled Shows how button states can be controlled by a user application. The program manages the disabled/enabled
states for buttons and changes the text color to show greyed-out (disabled) buttons states for buttons and changes the text color to show greyed-out (disabled) buttons
""" """
sg.theme('Dark') sg.ChangeLookAndFeel('Dark')
sg.set_options(element_padding=(0, 0)) sg.SetOptions(element_padding=(0,0))
layout = [[sg.Text('User:', pad=((3, 0), 0)), sg.OptionMenu(values=('User 1', 'User 2'), size=(20, 1)), sg.Text('0', size=(8, 1))], layout = [[sg.T('User:', pad=((3,0),0)), sg.OptionMenu(values = ('User 1', 'User 2'), size=(20,1)), sg.T('0', size=(8,1))],
[sg.Text('Customer:', pad=((3, 0), 0)), sg.OptionMenu( [sg.T('Customer:', pad=((3,0),0)), sg.OptionMenu(values=('Customer 1', 'Customer 2'), size=(20,1)), sg.T('1', size=(8,1))],
values=('Customer 1', 'Customer 2'), size=(20, 1)), sg.Text('1', size=(8, 1))], [sg.T('Notes:', pad=((3,0),0)), sg.In(size=(44,1), background_color='white', text_color='black')],
[sg.Text('Notes:', pad=((3, 0), 0)), sg.Input(size=(44, 1), [sg.ReadButton('Start', button_color=('white', 'black'), key='_Start_'),
background_color='white', text_color='black')], sg.ReadButton('Stop', button_color=('white', 'black'), key='_Stop_'),
[sg.Button('Start', button_color=('white', 'black'), key='-Start-'), sg.ReadButton('Reset', button_color=('white', 'firebrick3'), key='_Reset_'),
sg.Button('Stop', button_color=('white', 'black'), key='-Stop-'), sg.ReadButton('Submit', button_color=('white', 'springgreen4'), key='_Submit_')]]
sg.Button('Reset', button_color=('white', 'firebrick3'), key='-Reset-'),
sg.Button('Submit', button_color=('white', 'springgreen4'), key='-Submit-')]]
window = sg.Window("Time Tracker", layout, window = sg.Window("Time Tracker", default_element_size=(12,1), text_justification='r', auto_size_text=False, auto_size_buttons=False,
default_element_size=(12, 1), default_button_element_size=(12,1)).Layout(layout).Finalize()
text_justification='r',
auto_size_text=False,
auto_size_buttons=False,
default_button_element_size=(12, 1),
finalize=True)
for key, state in {'-Start-': False, '-Stop-': True, '-Reset-': True, '-Submit-': True}.items(): for key, state in {'_Start_': False, '_Stop_': True, '_Reset_': True, '_Submit_': True}.items():
window[key].update(disabled=state) window.FindElement(key).Update(disabled=state)
recording = have_data = False recording = have_data = False
while True: while True:
event, values = window.read() event, values = window.Read()
print(event) print(event)
if event == sg.WIN_CLOSED: if event is None:
break sys.exit(69)
if event == '-Start-': if event == '_Start_':
for key, state in {'-Start-': True, '-Stop-': False, '-Reset-': False, '-Submit-': True}.items(): for key, state in {'_Start_':True, '_Stop_':False, '_Reset_':False, '_Submit_':True}.items():
window[key].update(disabled=state) window.FindElement(key).Update(disabled=state)
recording = True recording = True
elif event == '-Stop-' and recording: elif event == '_Stop_' and recording:
[window[key].update(disabled=value) for key, value in { [window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':False, '_Submit_':False}.items()]
'-Start-': False, '-Stop-': True, '-Reset-': False, '-Submit-': False}.items()]
recording = False recording = False
have_data = True have_data = True
elif event == '-Reset-': elif event == '_Reset_':
[window[key].update(disabled=value) for key, value in { [window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':True, '_Submit_':True}.items()]
'-Start-': False, '-Stop-': True, '-Reset-': True, '-Submit-': True}.items()]
recording = False recording = False
have_data = False have_data = False
elif event == '-Submit-' and have_data: elif event is '_Submit_' and have_data:
[window[key].update(disabled=value) for key, value in { [window.FindElement(key).Update(disabled=value) for key,value in {'_Start_':False, '_Stop_':True, '_Reset_':True, '_Submit_':False}.items()]
'-Start-': False, '-Stop-': True, '-Reset-': True, '-Submit-': False}.items()]
recording = False recording = False
window.close()

File diff suppressed because one or more lines are too long

View file

@ -1,113 +0,0 @@
"""
Demo - Button Toggle #2
Uses the IGNORE BUTTON setting.
When creating or updating buttons in 4.35.0+, you can use the parameter:
disabled=BUTTON_DISABLED_MEANS_IGNORE
This will cause the buytton to ignore your clicks but it won't change the button
with the GUI, which would change the color and gray it out. The button will not
change appearance at all. It will no longer respond to button clicks.
Another toggle button using Base64 strings
Of course files could be used instead of Base64 strings
The differences between this toggle button demo and the other one are:
1. Different button graphics
2. The button state is stored in the metadata for the button
3. The metadata for the button is a class instead of a single variable name
For buttons with graphics, it's generally best to set the button's:
* border width=0
* color = (background_color, background_color)
Buttons don't normally have explicit keys. However, since this button has
no text, there is no default key. It's better to be explicit with buttons that
change text or have graphics.
Copyright 2021 PySimpleGUI
"""
import PySimpleGUI as sg
import random
# Class holding the button graphic info. At this time only the state is kept
class BtnInfo:
def __init__(self, state=True):
self.state = state # Can have 3 states - True, False, None (disabled)
# Main function that creates the layout, window and has event loop
def main():
layout = [[sg.Text('Toggle Button')],
[sg.T('Disabled with PySimpleGUI Ignore:', text_color='yellow')],
[sg.Button(image_data=on_image, k='-TOGGLE1-', border_width=0,
button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled_button_color=(sg.theme_background_color(), sg.theme_background_color()),
metadata=BtnInfo()),
sg.T('Disable:'),
sg.Button(image_data=off_image, k='-DISABLE1-', border_width=0,
button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled_button_color=(sg.theme_background_color(), sg.theme_background_color()),
metadata=BtnInfo(False)), sg.T('Disabled button color is\nbetter than other disabled button below')
],
[sg.Button(image_data=on_image, k='-TOGGLE2-', border_width=0,
button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled_button_color=(sg.theme_background_color(), sg.theme_background_color()),
metadata=BtnInfo()),
sg.Image(data=sg.EMOJI_BASE64_HAPPY_THUMBS_UP,enable_events=True, k='-I-')
],
[ sg.T('Disabled with GUI:', text_color='yellow')],
[sg.Button(image_data=on_image, k='-TOGGLE3-', border_width=0,
button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled_button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled=True, metadata=BtnInfo()), sg.T('Note color has crosshatching')],
[ sg.T('Disabled with PySimpleGUI (ignored):', text_color='yellow')],
[sg.Button(image_data=on_image, k='-TOGGLE4-', border_width=0,
button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled_button_color=(sg.theme_background_color(), sg.theme_background_color()),
disabled=sg.BUTTON_DISABLED_MEANS_IGNORE,
metadata=BtnInfo())],
[sg.T(size=(40,1), k='-STATUS-')],
[sg.Button('Exit')]]
window = sg.Window('Window Title', layout, font='_ 14', finalize=True)
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
# Where all the magic happens. 2 things happen when button is clicked
# 1. The state toggles
# 2. The buton graphic changes
if 'TOGGLE' in event:
window[event].metadata.state = not window[event].metadata.state
window[event].update(image_data=on_image if window[event].metadata.state else off_image)
elif event == '-DISABLE1-':
window[event].metadata.state = not window[event].metadata.state
window[event].update(image_data=on_image if window[event].metadata.state else off_image)
window['-I-'].update(data=sg.EMOJI_BASE64_HAPPY_GASP if window[event].metadata.state else random.choice(sg.EMOJI_BASE64_HAPPY_LIST))
# if disabling the button
if window[event].metadata.state:
if window['-TOGGLE1-'].metadata.state is True:
window['-TOGGLE1-'].update(disabled=sg.BUTTON_DISABLED_MEANS_IGNORE, image_data=on_image_disabled)
elif window['-TOGGLE1-'].metadata.state is False:
window['-TOGGLE1-'].update(disabled=sg.BUTTON_DISABLED_MEANS_IGNORE, image_data=off_image_disabled)
else:
if window['-TOGGLE1-'].metadata.state is True:
window['-TOGGLE1-'].update(disabled=False, image_data=on_image)
elif window['-TOGGLE1-'].metadata.state is False:
window['-TOGGLE1-'].update(disabled=False, image_data=off_image)
window['-STATUS-'].update(f'event {event} button state = {window[event].metadata.state if window[event].metadata is not None else "Not applicable"}')
window.close()
# Define the button graphic base 64 strings and then call the main function
if __name__ == '__main__':
on_image = b'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'
off_image = b'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'
off_image_disabled = b'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'
on_image_disabled = b'iVBORw0KGgoAAAANSUhEUgAAAFoAAAAnCAYAAACPFF8dAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAVLSURBVGhD7Zu7TytHFMbHyxvsJeb9kjCiCihRpJAobaqblFGUJmXqKMofkyZpaKhS5HaJIgUpRZQmoqAhSgEUSBZgsAE/uJhX5ht80LmH8e56fdde7PuTPu0yj/XMt7Nnd2aXxPr6uuKMjIx84LruR47jJGtJbeeVplQqOaOjo+8MDAwk7u7uyrWsWIF2FYvFf3Rbt/HnQ+oDj0Ynk8kPl5eXf9Amf6L7pW5vb9X9/b3Jaye5XE719fWpubk51dPTY/bjijba+KbN3t7d3f324uLir1rWg9HpdPrFysrKy0KhMJTNZtX19XUtu/0sLi6qyclJlUqlcLWpRCJRy4knNzc3ShusKpXKq52dnS/z+fyvSE9sbGxMrq2t/Xd8fJw+PDw0hf1oRWdxNY2Pj6tMJqMmJiZUf3//Y3ocrjQJPOG+nJ2dYWSXt7a23tMRYt+Zn5//rlqteppMB5EHi5rZ2VmEtEeTAUzGJRo3yZOv7ydo94j293v8ndjW6JDxvh7RpoBEGtsKo9FofdNTq6urampqSvX29tZynhcIIUdHR//qUb3iDA4OZnDzs0Gm0khulQCMBs/VZIC2Dw8Pv6v71OvoO7lri3nUYb5tlToRp7Z9Deos37ZanYbVaA7vON/qCU1k6kQC94oMhxFk+FuCU9doPnptkPFRqBN5YjTvKO1LE3iZtwSjMwNiDGnYaD6aEa/1czieFdXQ0JB1wQfPw5C8Cii9Wwg9omHw2NiYmSLDaCz4YNoJ8ScHpGNBCGU4SIe6hVBGY+0BBmOiUy6XzQIKpptY9cOohrESjHg+y+u2ON+w0TAXpgGYfHl5aZYGq9WqMRsLLDDbNnXGyelWQsVoisUwl4OTQGvZPF5TOsxHyOlGQsdogNEroTQZGkqlktkiLnfq7M+LpnpsM4zS5EIVXvFUKhVzAmC2zH+OoA/1JGnYaByEwoN8PONhBXFbgngOw1GvnaNamhJWjdBwb2EmDAP0/EwvTV3XNQbiRNDJ4KBxuIGGQXayGXlhKx9WnFDDCjdBGEZhIJ1Om+dnmI2RXCwWayWfgrpXV1e1v4IhG10P2dEwCoKtnpQkVOgAGNX5fN7c5LCP+IvHOzxT85sk0uUoxt+oh7ygyI7Y5IetTlSSNBUoYSheg8E4mCYf9wDy5asyqlfvFZrE1pFGhd+0pYdRPbzKPTGaF6B9WVEeJGro95uRH7Y6jcqLuiOaKvIDyP2oFBRb3bDywlbeT5LAocPvQFEif5sUBFu9RuVHkDq+RvOK/ECIeW8y7nHZsJULIj9sdRpVEKxGU2W+lftRywtb+bDywlY+qCTGaLkuAagw39pGcBSjWoJJkFe+hJdtRn7Y6kBAznwdZPCVNg5V4gegfS4KI29KgB4VMWVHo7nZtjpcvG1hZTuulK0eID/RdpQDjn7+PcfMrh5UGciDRiVA69w03UfjMdVHw9EB5EUp/IaXbHXQdrwUQTsB2q5nwZc6/T6xubn5WyaT+Wxvb08VCgVTwAtbmIkCNHpmZkYtLCyY76P5iwQ6GXGE/MHMFzPlg4ODP/f39z91Tk9Pfzw/P1dLS0tqenra10h0shUC+JQYbTs5OXltfQRtjKvQdhhMyuVyP5k244t/PXJ+0aPmCywM4dLEohAuD1S0QUa0ApiMD9LxMTrCB1SvXe0GnuHegi1M1m3/I5vNvtBZd8Zo3fCkNvvnZDL5OV41Ic7EqTM48RjReOdo+3QhLmAAwmis4ejQ8bu+Ir/SaWYpk/9XViKVSn3tuu43ujMf67t8975JDYk29UrfAP/WA2NdawNJDzlK/Q9RjPZ1HEiBtwAAAABJRU5ErkJggg=='
main()

View file

@ -1,37 +0,0 @@
import PySimpleGUI as sg
"""
Toggle Button Demo - Simple Version
A simple graphic that toggles.
The "State" of on/off is stored in the button's Metadata
Copyright 2021 PySimpleGUI
"""
def main():
layout = [[sg.Text('A toggle button example')],
[sg.Text('Off'),
sg.Button(image_data=toggle_btn_off, key='-TOGGLE-GRAPHIC-', button_color=(sg.theme_background_color(), sg.theme_background_color()), border_width=0, metadata=False),
sg.Text('On')]]
window = sg.Window('Toggle Button Simple Graphic', layout)
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == '-TOGGLE-GRAPHIC-': # if the graphical button that changes images
window['-TOGGLE-GRAPHIC-'].metadata = not window['-TOGGLE-GRAPHIC-'].metadata
window['-TOGGLE-GRAPHIC-'].update(image_data=toggle_btn_on if window['-TOGGLE-GRAPHIC-'].metadata else toggle_btn_off)
window.close()
if __name__ == '__main__':
# The base64 strings for the button images
toggle_btn_off = b'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAED0lEQVRYCe1WTWwbRRR+M/vnv9hO7BjHpElMKSlpqBp6gRNHxAFVcKM3qgohQSqoqhQ45YAILUUVDRxAor2VAweohMSBG5ciodJUSVqa/iikaePEP4nj2Ovdnd1l3qqJksZGXscVPaylt7Oe/d6bb9/svO8BeD8vA14GvAx4GXiiM0DqsXv3xBcJU5IO+RXpLQvs5yzTijBmhurh3cyLorBGBVokQG9qVe0HgwiXLowdy9aKsY3g8PA5xYiQEUrsk93JTtjd1x3siIZBkSWQudUK4nZO1w3QuOWXV+HuP/fL85klAJuMCUX7zPj4MW1zvC0Ej4yMp/w++K2rM9b70sHBYCjo34x9bPelsgp/XJksZ7KFuwZjr3732YcL64ttEDw6cq5bVuCvgy/sje7rT0sI8PtkSHSEIRIKgCQKOAUGM6G4VoGlwiqoVd2Za9Vl8u87bGJqpqBqZOj86eEHGNch+M7otwHJNq4NDexJD+59RiCEQG8qzslFgN8ibpvZNsBifgXmFvJg459tiOYmOElzYvr2bbmkD509e1ylGEZk1Y+Ssfan18n1p7vgqVh9cuiDxJPxKPT3dfGXcN4Tp3dsg/27hUQs0qMGpRMYjLz38dcxS7Dm3nztlUAb38p0d4JnLozPGrbFfBFm79c8hA3H2AxcXSvDz7/+XtZE1kMN23hjV7LTRnKBh9/cZnAj94mOCOD32gi2EUw4FIRUMm6LGhyiik86nO5NBdGRpxYH14bbjYfJteN/OKR7UiFZVg5T27QHYu0RBxoONV9W8KQ7QVp0iXdE8fANUGZa0QAvfhhXlkQcmjJZbt631oIBnwKmacYoEJvwiuFgWncWnXAtuVBBEAoVVXWCaQZzxmYuut68b631KmoVBEHMUUrJjQLXRAQVSxUcmrKVHfjWWjC3XOT1FW5QrWpc5IJdQhDKVzOigEqS5dKHMVplnNOqrmsXqUSkn+YzWaHE9RW1FeXL7SKZXBFUrXW6jIV6YTEvMAUu0W/G3kcxPXP5ylQZs4fa6marcWvvZfJu36kuHjlc/nMSuXz+/ejxgqPFpuQ/xVude9eu39Jxu27OLvBGoMjrUN04zrNMbgVmOBZ96iPdPZmYntH5Ls76KuxL9NyoLA/brav7n382emDfHqeooXyhQmARVhSnAwNNMx5bu3V1+habun5nWdXhwJZ2C5mirTesyUR738sv7g88UQ0rEkTDlp+1wwe8Pf0klegUenYlgyg7bby75jUTITs2rhCAXXQ2vwxz84vlB0tZ0wL4NEcLX/04OrrltG1s8aOrHhk51SaK0us+n/K2xexBxljcsm1n6x/Fuv1PCWGiKOaoQCY1Vb9gWPov50+fdEqd21ge3suAlwEvA14G/ucM/AuppqNllLGPKwAAAABJRU5ErkJggg=='
toggle_btn_on = b'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAD+UlEQVRYCe1XzW8bVRCffbvrtbP+2NhOD7GzLm1VoZaPhvwDnKBUKlVyqAQ3/gAkDlWgPeVQEUCtEOIP4AaHSI0CqBWCQyXOdQuRaEFOk3g3IMWO46+tvZ+PeZs6apq4ipON1MNafrvreTPzfvub92bGAOEnZCBkIGQgZOClZoDrh25y5pdjruleEiX+A+rCaQo05bpuvJ/+IHJCSJtwpAHA/e269g8W5RbuzF6o7OVjF8D3Pr4tSSkyjcqfptPDMDKSleW4DKIggIAD5Yf+Oo4DNg6jbUBlvWLUNutAwZu1GnDjzrcXzGcX2AHw/emFUV6Sfk0pqcKpEydkKSo9q3tkz91uF5aWlo1Gs/mYc+i7tz4//19vsW2AU9O381TiioVCQcnlRsWeQhD3bJyH1/MiFLICyBHiuzQsD1arDvypW7DR9nzZmq47q2W95prm+I9fXfqXCX2AF2d+GhI98Y8xVX0lnxvl2UQQg0csb78ag3NjEeD8lXZ7pRTgftmCu4864OGzrq+5ZU0rCa3m+NzXlzvoAoB3+M+SyWQuaHBTEzKMq/3BMbgM+FuFCDBd9kK5XI5PJBKqLSev+POTV29lKB8rT0yMD0WjUSYLZLxzNgZvIHODOHuATP72Vwc6nQ4Uiw8MUeBU4nHS5HA6TYMEl02wPRcZBJuv+ya+UCZOIBaLwfCwQi1Mc4QXhA+PjWRkXyOgC1uIhW5Qd8yG2TK7kSweLcRGKKVnMNExWWBDTQsH9qVmtmzjiThQDs4Qz/OUSGTwcLwIQTLW58i+yOjpXDLqn1tgmDzXzRCk9eDenjo9yhvBmlizrB3V5dDrNTuY0A7opdndStqmaQLPC1WCGfShYRgHdLe32UrV3ntiH9LliuNrsToNlD4kruN8v75eafnSgC6Luo2+B3fGKskilj5muV6pNhk2Qqg5v7lZ51nBZhNBjGrbxfI1+La5t2JCzfD8RF1HTBGJXyDzs1MblONulEqPDVYXgwDIfNx91IUVbAbY837GMur+/k/XZ75UWmJ77ou5mfM1/0x7vP1ls9XQdF2z9uNsPzosXPNFA5m0/EX72TBSiqsWzN8z/GZB08pWq9VeEZ+0bjKb7RTD2i1P4u6r+bwypo5tZUumEcDAmuC3W8ezIqSGfE6g/sTd1W5p5bKjaWubrmWd29Fu9TD0GlYlmTx+8tTJoZeqYe2BZC1/JEU+wQR5TVEUPptJy3Fs+Vkzgf8lemqHumP1AnYoMZSwsVEz6o26i/G9Lgitb+ZmLu/YZtshfn5FZDPBCcJFQRQ+8ih9DctOFvdLIKHH6uUQnq9yhFu0bec7znZ+xpAGmuqef5/wd8hAyEDIQMjAETHwP7nQl2WnYk4yAAAAAElFTkSuQmCC'
main()

File diff suppressed because one or more lines are too long

View file

@ -1,32 +0,0 @@
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
play = b'iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAByElEQVRoge3ZMWsUQRjG8Z8RFSKCgoJp0qSJjVpoZ2clkk8g5CtYpU+TD5DSUkvbVCFNYiM2dhZqY6GFQooEISGai8Xu4HgmcnM3c+su+4fj2L2dmedhb+Z95x16enp6hljBxaZF5OAE7/GoaSGTchJ9tnCrWTnjE0zs19+HWMPlJkWNQzAyh2c4rq+/YBnnmpOWRjASuIfX0f0d3GlAVzLDRmBG9Ta+1r8d4wVuTFdaGqcZCVzFOn7Uz+ziKc5PR1oa/zISWMRm9OxbPCisK5lRjASW8Clqs4H5MrLSSTECs1jFQd3ue319KbewVFKNBBbwMmr/EY8z6kpmXCOBh3gX9dNYdjCpEbigWs326r6OVKvdlQn7TSKHkcCcKt4MNJAd5DQSuI83Ud87uJ15jL8oYYTf2cE3f2YH1wuMhXJGAtdU8+WnwtlBaSOBu3gVjZc9O5iWEapJ/wSf6zEHeI6bZzWYmY6u/4v+rzUirZ/snVh+hwPitpYFxNanKJ1IGk9L4xcz6Eom18bqg5ZtrDqx1Y2LDwPVG2lV8aH15aDWF+jOKpkWi8o5GKWIXTwq56BzxwqdOejpxNFbJw5DO3M83dPT02J+AbN50HbYDxzCAAAAAElFTkSuQmCC'
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')
# 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)] ]
# Create the window
window = sg.Window('Simple Base64 Buttons', 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
break
window.close() # Exiting so clean up

File diff suppressed because one or more lines are too long

View file

@ -1,44 +1,50 @@
#!/usr/bin/env python #!/usr/bin/env python
import sys import sys
import time import time
import PySimpleGUI as sg if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
def show_win(): def show_win():
sg.set_options(border_width=0, margins=(0, 0), element_padding=(5, 3)) sg.SetOptions(border_width=0, margins=(0,0), element_padding=(5,3))
frame_layout = [
[sg.Button(image_data=mac_red,
button_color=(sg.theme_background_color(),sg.theme_background_color()), key='-exit-'),
sg.Button('', image_data=mac_orange,
button_color=(sg.theme_background_color(),sg.theme_background_color())),
sg.Button('', image_data=mac_green,
button_color=(sg.theme_background_color(),sg.theme_background_color()), key='-minimize-'),
sg.Text(' '*40)], ]
layout = [[sg.Frame('', frame_layout)], frame_layout = [ [sg.Button('', image_data=mac_red, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_exit_'),
[sg.Text('')], sg.Button('', image_data=mac_orange, button_color=('white', sg.COLOR_SYSTEM_DEFAULT)),
[sg.Text('My Mac-alike window', size=(25, 2))], ] sg.Button('', image_data=mac_green, button_color=('white', sg.COLOR_SYSTEM_DEFAULT), key='_minimize_'),
sg.Text(' '*40)],]
window = sg.Window('My new window', layout, layout = [[sg.Frame('',frame_layout)],
no_titlebar=True, grab_anywhere=True, [sg.T('')],
alpha_channel=0, finalize=True) [ sg.Text(' My Mac-alike window', size=(25,2)) ],]
window = sg.Window('My new window',
no_titlebar=True,
grab_anywhere=True,
alpha_channel=0,
).Layout(layout).Finalize()
for i in range(100): for i in range(100):
window.set_alpha(i/100) window.SetAlpha(i/100)
time.sleep(.01) time.sleep(.01)
while True: # Event Loop while True: # Event Loop
event, values = window.read() event, values = window.Read()
if event == sg.WIN_CLOSED or event == '-exit-': if event is None or event == '_exit_':
break break
if event == '-minimize-': if event == '_minimize_':
# window.Minimize() # cannot minimize a window with no titlebar # window.Minimize() # cannot minimize a window with no titlebar
pass pass
print(event, values) print(event, values)
mac_red = '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' mac_red = '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'
mac_green = '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' mac_green = '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'
mac_orange = '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' mac_orange = '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'
show_win() show_win()

File diff suppressed because one or more lines are too long

View file

@ -1,54 +0,0 @@
#!/usr/bin/env python
"""
Demo - Realtime Buttons
Realtime buttons provide a way for you to get a continuous stream of button
events for as long as a button is held down.
This demo is using a timeout to determine that a button has been released.
If your application doesn't care when a button is released and only needs to know
that it's being held down, then you can remove the timeout on the window read call.
Note that your reaction latency will be the same as your timeout value. In this demo
the timeout is 100, so there will be 100ms between releasing a button and your program detecting
this has happened.
Copyright 2021 PySimpleGUI
"""
import PySimpleGUI as sg
def main():
# The Quit button is being placed in the bottom right corner and the colors are inverted, just for fun
layout = [[sg.Text('Robotics Remote Control')],
[sg.Text('Hold Down Button To Move')],
[sg.Text()],
[sg.Text(' '),
sg.RealtimeButton(sg.SYMBOL_UP, key='-FORWARD-')],
[sg.RealtimeButton(sg.SYMBOL_LEFT, key='-LEFT-'),
sg.Text(size=(10,1), key='-STATUS-', justification='c', pad=(0,0)),
sg.RealtimeButton(sg.SYMBOL_RIGHT, key='-RIGHT-')],
[sg.Text(' '),
sg.RealtimeButton(sg.SYMBOL_DOWN, key='-DOWN-')],
[sg.Text()],
[sg.Column([[sg.Quit(button_color=(sg.theme_button_color()[1], sg.theme_button_color()[0]), focus=True)]], justification='r')]]
window = sg.Window('Robotics Remote Control', layout)
while True:
# This is the code that reads and updates your window
event, values = window.read(timeout=100)
if event in (sg.WIN_CLOSED, 'Quit'):
break
if event != sg.TIMEOUT_EVENT:
# if not a timeout event, then it's a button that's being held down
window['-STATUS-'].update(event)
else:
# A timeout signals that all buttons have been released so clear the status display
window['-STATUS-'].update('')
window.close()
if __name__ == '__main__':
sg.theme('dark red')
main()

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

@ -1,24 +1,15 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
""" layout = [[sg.T('Calendar Test')],
Simple test harness to demonstate how to use the CalendarButton and the get date popup [sg.In('', size=(20,1), key='input')],
""" [sg.CalendarButton('Choose Date', target='input', key='date')],
# sg.theme('Dark Red') [sg.Ok(key=1)]]
layout = [[sg.Text('Date Chooser Test Harness', key='-TXT-')],
[sg.Input(key='-IN-', size=(20,1)), sg.CalendarButton('Cal US No Buttons Location (0,0)', close_when_date_chosen=True, target='-IN-', location=(0,0), no_titlebar=False, )],
[sg.Input(key='-IN3-', size=(20,1)), sg.CalendarButton('Cal Monday', title='Pick a date any date', no_titlebar=True, close_when_date_chosen=False, target='-IN3-', begin_at_sunday_plus=1, month_names=('студзень', 'люты', 'сакавік', 'красавік', 'май', 'чэрвень', 'ліпень', 'жнівень', 'верасень', 'кастрычнік', 'лістапад', 'снежань'), day_abbreviations=('Дш', 'Шш', 'Шр', 'Бш', 'Жм', 'Иш', 'Жш'))],
[sg.Input(key='-IN2-', size=(20,1)), sg.CalendarButton('Cal German Feb 2020', target='-IN2-', default_date_m_d_y=(2,None,2020), locale='de_DE', begin_at_sunday_plus=1 )],
[sg.Input(key='-IN4-', size=(20,1)), sg.CalendarButton('Cal Format %m-%d Jan 2020', target='-IN4-', format='%m-%d', default_date_m_d_y=(1,None,2020), )],
[sg.Button('Read'), sg.Button('Date Popup'), sg.Exit()]]
window = sg.Window('window', layout) window = sg.Window('Calendar', grab_anywhere=False).Layout(layout)
event,values = window.Read()
while True: sg.Popup(values['input'])
event, values = window.read()
print(event, values)
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event == 'Date Popup':
sg.popup('You chose:', sg.popup_get_date())
window.close()

View file

@ -1,18 +1,24 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [ layout = [
[sg.Canvas(size=(150, 150), background_color='red', key='canvas')], [sg.Canvas(size=(150, 150), background_color='red', key='canvas')],
[sg.Text('Change circle color to:'), sg.Button('Red'), sg.Button('Blue')] [sg.T('Change circle color to:'), sg.ReadButton('Red'), sg.ReadButton('Blue')]
] ]
window = sg.Window('Canvas test', layout, finalize=True) window = sg.Window('Canvas test').Layout(layout).Finalize()
cir = window['canvas'].TKCanvas.create_oval(50, 50, 100, 100) cir = window.FindElement('canvas').TKCanvas.create_oval(50, 50, 100, 100)
while True: while True:
event, values = window.read() event, values = window.Read()
if event == sg.WIN_CLOSED: if event is None:
break break
if event in ('Blue', 'Red'): if event is 'Blue':
window['canvas'].TKCanvas.itemconfig(cir, fill=event) window.FindElement('canvas').TKCanvas.itemconfig(cir, fill = "Blue")
elif event is 'Red':
window.FindElement('canvas').TKCanvas.itemconfig(cir, fill = "Red")

View file

@ -0,0 +1,48 @@
#!/usr/bin/env python
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
"""
Demonstrates the new change_submits parameter for inputtext elements
It ONLY submits when a button changes the field, not normal user input
Be careful on persistent forms to not clear the input
"""
layout = [[ sg.Text('Test of reading input field') ],
[sg.T('This input is normal'), sg.In()],
[sg.T('This input change submits'), sg.In(change_submits=True)],
[sg.T('This multiline input change submits'), sg.Multiline(change_submits=True, do_not_clear=True)],
[sg.T('This input is normal'), sg.In(), sg.FileBrowse()],
[sg.T('File Browse submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in1_'), sg.FileBrowse()],
[sg.T('Color Chooser submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in2_'), sg.ColorChooserButton('Color...', target=(sg.ThisRow, -1))],
[sg.T('Folder Browse submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in3_'), sg.FolderBrowse()],
[sg.T('Calendar Chooser submits'), sg.In(change_submits=True,
do_not_clear=True,
key='_in4_'), sg.CalendarButton('Date...', target=(sg.ThisRow, -1))],
[sg.T('Disabled input submits'), sg.In(change_submits=True,
do_not_clear=True,
disabled=True,
key='_in5'), sg.FileBrowse()],
[sg.T('This input clears after submit'),sg.In(change_submits=True,
key='_in6_'), sg.FileBrowse()],
[ sg.Button('Read')]]
window = sg.Window('Demonstration of InputText with change_submits',
auto_size_text=False,
default_element_size=(22,1),
text_justification='right',
).Layout(layout)
while True: # Event Loop
event, values = window.Read()
print(event, values)
if event is None:
break

View file

@ -1,31 +1,41 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
''' '''
A simple send/response chat window. Add call to your send-routine and print the response A chat window. Add call to your send-routine, print the response and you're done
If async responses can come in, then will need to use a different design that uses PySimpleGUI async design pattern
Copyright 2023 PySimpleGUI To see this program RUN on the web go here:
https://repl.it/@PySimpleGUI/Chat-Application-Demo
Note that the size of the display on repl.it is smaller than most, so the sizes of the
Multiline and Output text areas were reduced in the online version. Nothing else was changed
''' '''
sg.theme('GreenTan') # give our window a spiffy set of colors sg.ChangeLookAndFeel('GreenTan') # give our window a spiffy set of colors
layout = [[sg.Text('Your output will go here', size=(40, 1))], layout = [ [sg.Text('Your output will go here', size=(40, 1))],
[sg.Output(size=(110, 20), font=('Helvetica 10'))], [sg.Output(size=(127, 30), font=('Helvetica 10'))],
[sg.Multiline(size=(70, 5), enter_submits=True, key='-QUERY-', do_not_clear=False), [sg.Multiline(size=(85, 5), enter_submits=True, key='query'),
sg.Button('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True), 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]))]] 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) window = sg.Window('Chat window',
default_element_size=(30, 2),
font=('Helvetica',' 13'),
default_button_element_size=(8,2)).Layout(layout)
while True: # The Event Loop # ---===--- Loop taking in user input and using it --- #
event, values = window.read() while True:
if event in (sg.WIN_CLOSED, 'EXIT'): # quit if exit button or X event, value = window.Read()
break if event is 'SEND':
if event == 'SEND': query = value['query'].rstrip()
query = values['-QUERY-'].rstrip()
# EXECUTE YOUR COMMAND HERE # EXECUTE YOUR COMMAND HERE
print('The command you entered was {}'.format(query), flush=True) print('The command you entered was {}'.format(query))
elif event in (None, 'EXIT'): # quit if exit button or X
break
sys.exit(69)
window.close()

View file

@ -1,5 +1,9 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
''' '''
A chatbot with history A chatbot with history
@ -11,60 +15,46 @@ Special keyboard keys:
Control C - exit form Control C - exit form
''' '''
def ChatBotWithHistory(): def ChatBotWithHistory():
# ------- Make a new Window ------- # # ------- Make a new Window ------- #
# give our form a spiffy set of colors sg.ChangeLookAndFeel('GreenTan') # give our form a spiffy set of colors
sg.theme('GreenTan')
layout = [[sg.Text('Your output will go here', size=(40, 1))], layout = [[sg.Text('Your output will go here', size=(40, 1))],
[sg.Output(size=(127, 30), font=('Helvetica 10'))], [sg.Output(size=(127, 30), font=('Helvetica 10'))],
[sg.Text('Command History'), [sg.T('Command History'), sg.T('', size=(20,3), key='history')],
sg.Text('', size=(20, 3), key='history')], [sg.Multiline(size=(85, 5), enter_submits=True, key='query', do_not_clear=False),
[sg.ML(size=(85, 5), enter_submits=True, key='query', do_not_clear=False), sg.ReadButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0]), bind_return_key=True),
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]))]] sg.Button('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
window = sg.Window('Chat window with history', layout, window = sg.Window('Chat window with history', default_element_size=(30, 2), font=('Helvetica',' 13'), default_button_element_size=(8,2), return_keyboard_events=True).Layout(layout)
default_element_size=(30, 2),
font=('Helvetica', ' 13'),
default_button_element_size=(8, 2),
return_keyboard_events=True)
# ---===--- Loop taking in user input and using it --- # # ---===--- Loop taking in user input and using it --- #
command_history = [] command_history = []
history_offset = 0 history_offset = 0
while True: while True:
event, value = window.read() (event, value) = window.Read()
if event is 'SEND':
if event == 'SEND':
query = value['query'].rstrip() query = value['query'].rstrip()
# EXECUTE YOUR COMMAND HERE # EXECUTE YOUR COMMAND HERE
print('The command you entered was {}'.format(query)) print('The command you entered was {}'.format(query))
command_history.append(query) command_history.append(query)
history_offset = len(command_history)-1 history_offset = len(command_history)-1
# manually clear input because keyboard events blocks clear window.FindElement('query').Update('') # manually clear input because keyboard events blocks clear
window['query'].update('') window.FindElement('history').Update('\n'.join(command_history[-3:]))
window['history'].update('\n'.join(command_history[-3:])) elif event is None or event is 'EXIT': # quit if exit event or X
elif event in (sg.WIN_CLOSED, 'EXIT'): # quit if exit event or X
break break
elif 'Up' in event and len(command_history): elif 'Up' in event and len(command_history):
command = command_history[history_offset] command = command_history[history_offset]
# decrement is not zero history_offset -= 1 * (history_offset > 0) # decrement is not zero
history_offset -= 1 * (history_offset > 0) window.FindElement('query').Update(command)
window['query'].update(command)
elif 'Down' in event and len(command_history): elif 'Down' in event and len(command_history):
# increment up to end of list history_offset += 1 * (history_offset < len(command_history)-1) # increment up to end of list
history_offset += 1 * (history_offset < len(command_history)-1)
command = command_history[history_offset] command = command_history[history_offset]
window['query'].update(command) window.FindElement('query').Update(command)
elif 'Escape' in event: elif 'Escape' in event:
window['query'].update('') window.FindElement('query').Update('')
sys.exit(69)
ChatBotWithHistory() ChatBotWithHistory()

View file

@ -1,17 +1,16 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from chatterbot import ChatBot
import chatterbot.utils import chatterbot.utils
''' '''
Demo_Chatterbot.py Demo_Chatterbot.py
Note - this code was written using version 0.8.7 of Chatterbot... to install:
python -m pip install chatterbot==0.8.7
It still runs fine with the old version.
A GUI wrapped arouind the Chatterbot package. A GUI wrapped arouind the Chatterbot package.
The GUI is used to show progress bars during the training process and The GUI is used to show progress bars during the training process and
to collect user input that is sent to the chatbot. The reply is displayed in the GUI window to collect user input that is sent to the chatbot. The reply is displayed in the GUI window
@ -19,81 +18,59 @@ to collect user input that is sent to the chatbot. The reply is displayed in th
# Create the 'Trainer GUI' # Create the 'Trainer GUI'
# The Trainer GUI consists of a lot of progress bars stacked on top of each other # The Trainer GUI consists of a lot of progress bars stacked on top of each other
sg.theme('GreenTan') sg.ChangeLookAndFeel('GreenTan')
# sg.DebugWin() # sg.DebugWin()
MAX_PROG_BARS = 20 # number of training sessions MAX_PROG_BARS = 20 # number of training sessions
bars = [] bars = []
texts = [] texts = []
training_layout = [[sg.Text('TRAINING PROGRESS', size=(20, 1), font=('Helvetica', 17))]] training_layout = [[sg.T('TRAINING PROGRESS', size=(20, 1), font=('Helvetica', 17))], ]
for i in range(MAX_PROG_BARS): for i in range(MAX_PROG_BARS):
bars.append(sg.ProgressBar(100, size=(30, 4))) bars.append(sg.ProgressBar(100, size=(30, 4)))
texts.append(sg.Text(' ' * 20, size=(20, 1), justification='right')) texts.append(sg.T(' ' * 20, size=(20, 1), justification='right'))
training_layout += [[texts[i], bars[i]], ] # add a single row training_layout += [[texts[i], bars[i]],] # add a single row
training_window = sg.Window('Training', training_layout) training_window = sg.Window('Training').Layout(training_layout)
current_bar = 0 current_bar = 0
# callback function for training runs # callback function for training runs
def print_progress_bar(description, iteration_counter, total_items, progress_bar_length=20): def print_progress_bar(description, iteration_counter, total_items, progress_bar_length=20):
global current_bar global current_bar
global bars global bars
global texts global texts
global training_window global training_window
# update the window and the bars # update the window and the bars
button, values = training_window.read(timeout=0) button, values = training_window.Read(timeout=0)
if button is None: # if user closed the window on us, exit if button is None: # if user closed the window on us, exit
return sys.exit(69)
if bars[current_bar].UpdateBar(iteration_counter, max=total_items) is False: if bars[current_bar].UpdateBar(iteration_counter, max=total_items) is False:
return sys.exit(69)
# show the training dataset name texts[current_bar].Update(description) # show the training dataset name
texts[current_bar].update(description)
if iteration_counter == total_items: if iteration_counter == total_items:
current_bar += 1 current_bar += 1
# redefine the chatbot text based progress bar with a graphical one # redefine the chatbot text based progress bar with a graphical one
chatterbot.utils.print_progress_bar = print_progress_bar chatterbot.utils.print_progress_bar = print_progress_bar
from chatterbot import ChatBot
from chatterbot.trainers import ChatterBotCorpusTrainer
chatbot = ChatBot('Ron Obvious')
# Create a new trainer for the chatbot
trainer = ChatterBotCorpusTrainer(chatbot)
# Train based on the english corpus
trainer.train("chatterbot.corpus.english")
# Train based on english greetings corpus
trainer.train("chatterbot.corpus.english.greetings")
# Train based on the english conversations corpus
trainer.train("chatterbot.corpus.english.conversations")
chatbot = ChatBot('Ron Obvious', trainer='chatterbot.trainers.ChatterBotCorpusTrainer') chatbot = ChatBot('Ron Obvious', trainer='chatterbot.trainers.ChatterBotCorpusTrainer')
# Train based on the english corpus # Train based on the english corpus
# chatbot.train("chatterbot.corpus.english") chatbot.train("chatterbot.corpus.english")
################# GUI ################# ################# GUI #################
layout = [[sg.Multiline(size=(80, 20), reroute_stdout=True, echo_stdout_stderr=True)], layout = [[sg.Output(size=(80, 20))],
[sg.MLine(size=(70, 5), key='-MLINE IN-', enter_submits=True, do_not_clear=False), [sg.Multiline(size=(70, 5), enter_submits=True),
sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]] sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]]
window = sg.Window('Chat Window', layout, window = sg.Window('Chat Window', auto_size_text=True, default_element_size=(30, 2)).Layout(layout)
default_element_size=(30, 2))
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
while True: while True:
event, values = window.read() event, (value,) = window.Read()
if event != 'SEND': if event is not 'SEND':
break break
string = values['-MLINE IN-'].rstrip() string = value.rstrip()
print(' ' + string) print(' '+string)
# send the user input to chatbot to get a response # send the user input to chatbot to get a response
response = chatbot.get_response(values['-MLINE IN-'].rstrip()) response = chatbot.get_response(value.rstrip())
print(response) print(response)

View file

@ -1,7 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
import PySimpleGUI as sg import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
from chatterbot import ChatBot from chatterbot import ChatBot
import chatterbot.utils import chatterbot.utils
from gtts import gTTS from gtts import gTTS
from pygame import mixer from pygame import mixer
import time import time
@ -9,14 +15,6 @@ import os
''' '''
Demo_Chatterbot.py Demo_Chatterbot.py
Note - this code was written using version 0.8.7 of Chatterbot... to install:
python -m pip install chatterbot==0.8.7
It still runs fine with the old version.
A GUI wrapped arouind the Chatterbot package. A GUI wrapped arouind the Chatterbot package.
The GUI is used to show progress bars during the training process and The GUI is used to show progress bars during the training process and
to collect user input that is sent to the chatbot. The reply is displayed in the GUI window to collect user input that is sent to the chatbot. The reply is displayed in the GUI window
@ -24,18 +22,18 @@ to collect user input that is sent to the chatbot. The reply is displayed in th
# Create the 'Trainer GUI' # Create the 'Trainer GUI'
# The Trainer GUI consists of a lot of progress bars stacked on top of each other # The Trainer GUI consists of a lot of progress bars stacked on top of each other
sg.theme('NeutralBlue') sg.ChangeLookAndFeel('NeutralBlue')
# sg.DebugWin() # sg.DebugWin()
MAX_PROG_BARS = 20 # number of training sessions MAX_PROG_BARS = 20 # number of training sessions
bars = [] bars = []
texts = [] texts = []
training_layout = [[sg.Text('TRAINING PROGRESS', size=(20, 1), font=('Helvetica', 17))], ] training_layout = [[sg.T('TRAINING PROGRESS', size=(20, 1), font=('Helvetica', 17))], ]
for i in range(MAX_PROG_BARS): for i in range(MAX_PROG_BARS):
bars.append(sg.ProgressBar(100, size=(30, 4))) bars.append(sg.ProgressBar(100, size=(30, 4)))
texts.append(sg.Text(' ' * 20, size=(20, 1), justification='right')) texts.append(sg.T(' ' * 20, size=(20, 1), justification='right'))
training_layout += [[texts[i], bars[i]],] # add a single row training_layout += [[texts[i], bars[i]],] # add a single row
training_window = sg.Window('Training', training_layout) training_window = sg.Window('Training').Layout(training_layout)
current_bar = 0 current_bar = 0
# callback function for training runs # callback function for training runs
@ -45,12 +43,12 @@ def print_progress_bar(description, iteration_counter, total_items, progress_bar
global texts global texts
global training_window global training_window
# update the window and the bars # update the window and the bars
button, values = training_window.read(timeout=0) button, values = training_window.Read(timeout=0)
if button is None: # if user closed the window on us, exit if button is None: # if user closed the window on us, exit
return sys.exit(69)
if bars[current_bar].update_bar(iteration_counter, max=total_items) is False: if bars[current_bar].UpdateBar(iteration_counter, max=total_items) is False:
return sys.exit(69)
texts[current_bar].update(description) # show the training dataset name texts[current_bar].Update(description) # show the training dataset name
if iteration_counter == total_items: if iteration_counter == total_items:
current_bar += 1 current_bar += 1
@ -80,22 +78,20 @@ chatbot.train("chatterbot.corpus.english")
################# GUI ################# ################# GUI #################
layout = [[sg.Multiline(size=(80, 20), reroute_stdout=True, echo_stdout_stderr=True)], layout = [[sg.Output(size=(80, 20))],
[sg.MLine(size=(70, 5), key='-MLINE IN-', enter_submits=True, do_not_clear=False), [sg.Multiline(size=(70, 5), enter_submits=True),
sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]] sg.Button('SEND', bind_return_key=True), sg.Button('EXIT')]]
window = sg.Window('Chat Window', layout, window = sg.Window('Chat Window', auto_size_text=True, default_element_size=(30, 2)).Layout(layout)
default_element_size=(30, 2))
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- # # ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
while True: while True:
event, values = window.read() event, (value,) = window.Read()
if event != 'SEND': if event is not 'SEND':
break break
string = values['-MLINE IN-'].rstrip() string = value.rstrip()
print(' ' + string) print(' '+string)
# send the user input to chatbot to get a response # send the user input to chatbot to get a response
response = chatbot.get_response(values['-MLINE IN-'].rstrip()) response = chatbot.get_response(value.rstrip())
print(response) print(response)
speak(str(response))
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'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAKMGlDQ1BJQ0MgUHJvZmlsZQAAeJydlndUVNcWh8+9d3qhzTAUKUPvvQ0gvTep0kRhmBlgKAMOMzSxIaICEUVEBBVBgiIGjIYisSKKhYBgwR6QIKDEYBRRUXkzslZ05eW9l5ffH2d9a5+99z1n733WugCQvP25vHRYCoA0noAf4uVKj4yKpmP7AQzwAAPMAGCyMjMCQj3DgEg+Hm70TJET+CIIgDd3xCsAN428g+h08P9JmpXBF4jSBInYgs3JZIm4UMSp2YIMsX1GxNT4FDHDKDHzRQcUsbyYExfZ8LPPIjuLmZ3GY4tYfOYMdhpbzD0i3pol5IgY8RdxURaXky3iWyLWTBWmcUX8VhybxmFmAoAiie0CDitJxKYiJvHDQtxEvBQAHCnxK47/igWcHIH4Um7pGbl8bmKSgK7L0qOb2doy6N6c7FSOQGAUxGSlMPlsult6WgaTlwvA4p0/S0ZcW7qoyNZmttbWRubGZl8V6r9u/k2Je7tIr4I/9wyi9X2x/ZVfej0AjFlRbXZ8scXvBaBjMwDy97/YNA8CICnqW/vAV/ehieclSSDIsDMxyc7ONuZyWMbigv6h/+nwN/TV94zF6f4oD92dk8AUpgro4rqx0lPThXx6ZgaTxaEb/XmI/3HgX5/DMISTwOFzeKKIcNGUcXmJonbz2FwBN51H5/L+UxP/YdiftDjXIlEaPgFqrDGQGqAC5Nc+gKIQARJzQLQD/dE3f3w4EL+8CNWJxbn/LOjfs8Jl4iWTm/g5zi0kjM4S8rMW98TPEqABAUgCKlAAKkAD6AIjYA5sgD1wBh7AFwSCMBAFVgEWSAJpgA+yQT7YCIpACdgBdoNqUAsaQBNoASdABzgNLoDL4Dq4AW6DB2AEjIPnYAa8AfMQBGEhMkSBFCBVSAsygMwhBuQIeUD+UAgUBcVBiRAPEkL50CaoBCqHqqE6qAn6HjoFXYCuQoPQPWgUmoJ+h97DCEyCqbAyrA2bwAzYBfaDw+CVcCK8Gs6DC+HtcBVcDx+D2+EL8HX4NjwCP4dnEYAQERqihhghDMQNCUSikQSEj6xDipFKpB5pQbqQXuQmMoJMI+9QGBQFRUcZoexR3qjlKBZqNWodqhRVjTqCakf1oG6iRlEzqE9oMloJbYC2Q/ugI9GJ6Gx0EboS3YhuQ19C30aPo99gMBgaRgdjg/HGRGGSMWswpZj9mFbMecwgZgwzi8ViFbAGWAdsIJaJFWCLsHuxx7DnsEPYcexbHBGnijPHeeKicTxcAa4SdxR3FjeEm8DN46XwWng7fCCejc/Fl+Eb8F34Afw4fp4gTdAhOBDCCMmEjYQqQgvhEuEh4RWRSFQn2hKDiVziBmIV8TjxCnGU+I4kQ9InuZFiSELSdtJh0nnSPdIrMpmsTXYmR5MF5O3kJvJF8mPyWwmKhLGEjwRbYr1EjUS7xJDEC0m8pJaki+QqyTzJSsmTkgOS01J4KW0pNymm1DqpGqlTUsNSs9IUaTPpQOk06VLpo9JXpSdlsDLaMh4ybJlCmUMyF2XGKAhFg+JGYVE2URoolyjjVAxVh+pDTaaWUL+j9lNnZGVkLWXDZXNka2TPyI7QEJo2zYeWSiujnaDdob2XU5ZzkePIbZNrkRuSm5NfIu8sz5Evlm+Vvy3/XoGu4KGQorBToUPhkSJKUV8xWDFb8YDiJcXpJdQl9ktYS4qXnFhyXwlW0lcKUVqjdEipT2lWWUXZSzlDea/yReVpFZqKs0qySoXKWZUpVYqqoypXtUL1nOozuizdhZ5Kr6L30GfUlNS81YRqdWr9avPqOurL1QvUW9UfaRA0GBoJGhUa3RozmqqaAZr5ms2a97XwWgytJK09Wr1ac9o62hHaW7Q7tCd15HV8dPJ0mnUe6pJ1nXRX69br3tLD6DH0UvT2693Qh/Wt9JP0a/QHDGADawOuwX6DQUO0oa0hz7DecNiIZORilGXUbDRqTDP2Ny4w7jB+YaJpEm2y06TX5JOplWmqaYPpAzMZM1+zArMus9/N9c1Z5jXmtyzIFp4W6y06LV5aGlhyLA9Y3rWiWAVYbbHqtvpobWPNt26xnrLRtImz2WczzKAyghiljCu2aFtX2/W2p23f2VnbCexO2P1mb2SfYn/UfnKpzlLO0oalYw7qDkyHOocRR7pjnONBxxEnNSemU73TE2cNZ7Zzo/OEi55Lsssxlxeupq581zbXOTc7t7Vu590Rdy/3Yvd+DxmP5R7VHo891T0TPZs9Z7ysvNZ4nfdGe/t57/Qe9lH2Yfk0+cz42viu9e3xI/mF+lX7PfHX9+f7dwXAAb4BuwIeLtNaxlvWEQgCfQJ3BT4K0glaHfRjMCY4KLgm+GmIWUh+SG8oJTQ29GjomzDXsLKwB8t1lwuXd4dLhseEN4XPRbhHlEeMRJpEro28HqUYxY3qjMZGh0c3Rs+u8Fixe8V4jFVMUcydlTorc1ZeXaW4KnXVmVjJWGbsyTh0XETc0bgPzEBmPXM23id+X/wMy421h/Wc7cyuYE9xHDjlnIkEh4TyhMlEh8RdiVNJTkmVSdNcN24192Wyd3Jt8lxKYMrhlIXUiNTWNFxaXNopngwvhdeTrpKekz6YYZBRlDGy2m717tUzfD9+YyaUuTKzU0AV/Uz1CXWFm4WjWY5ZNVlvs8OzT+ZI5/By+nL1c7flTuR55n27BrWGtaY7Xy1/Y/7oWpe1deugdfHrutdrrC9cP77Ba8ORjYSNKRt/KjAtKC94vSliU1ehcuGGwrHNXpubiySK+EXDW+y31G5FbeVu7d9msW3vtk/F7OJrJaYllSUfSlml174x+6bqm4XtCdv7y6zLDuzA7ODtuLPTaeeRcunyvPKxXQG72ivoFcUVr3fH7r5aaVlZu4ewR7hnpMq/qnOv5t4dez9UJ1XfrnGtad2ntG/bvrn97P1DB5wPtNQq15bUvj/IPXi3zquuvV67vvIQ5lDWoacN4Q293zK+bWpUbCxp/HiYd3jkSMiRniabpqajSkfLmuFmYfPUsZhjN75z/66zxailrpXWWnIcHBcef/Z93Pd3Tvid6D7JONnyg9YP+9oobcXtUHtu+0xHUsdIZ1Tn4CnfU91d9l1tPxr/ePi02umaM7Jnys4SzhaeXTiXd272fMb56QuJF8a6Y7sfXIy8eKsnuKf/kt+lK5c9L1/sdek9d8XhyumrdldPXWNc67hufb29z6qv7Sern9r6rfvbB2wGOm/Y3ugaXDp4dshp6MJN95uXb/ncun572e3BO8vv3B2OGR65y747eS/13sv7WffnH2x4iH5Y/EjqUeVjpcf1P+v93DpiPXJm1H2070nokwdjrLHnv2T+8mG88Cn5aeWE6kTTpPnk6SnPqRvPVjwbf57xfH666FfpX/e90H3xw2/Ov/XNRM6Mv+S/XPi99JXCq8OvLV93zwbNPn6T9mZ+rvitwtsj7xjvet9HvJ+Yz/6A/VD1Ue9j1ye/Tw8X0hYW/gUDmPP8uaxzGQAAAp1JREFUeJzFlk1rE1EUhp9z5iat9kMlVXGhKH4uXEo1CoIKrnSnoHs3unLnxpW7ipuCv0BwoRv/gCBY2/gLxI2gBcHGT9KmmmTmHBeTlLRJGquT+jJ3djPPfV/OPefK1UfvD0hIHotpsf7jm4mq4k6mEsEtsfz2gpr4rGpyPYjGjyUMFy1peNg5odkSV0nNDNFwxhv2JAhR0ZKGA0JiIAPCpgTczaVhRa1//2qoprhBQdv/LSKNasVUVAcZb/c9/A9oSwMDq6Rr08DSXNW68TN2pAc8U3CLsVQ3bpwocHb/CEs16+o8ZAoVWKwZNycLXD62DYDyUszbLzW2BMHa+lIm4Fa8lZpx6+QEl46OA1CaX+ZjpUFeV0MzAbecdoPen1lABHKRdHThdcECiNCx27XQxTXQufllHrxaIFKItBMK6xSXCCSeFsoKZO2m6AUtE0lvaE+wCPyKna055erx7SSWul7pes1Xpd4Z74OZhfQMrwOFLlELYAbjeeXuud0cKQyxZyzHw9efGQ6KStrve8WrCpHSd7J2gL1Jjx0qvxIALh4aIxJhulRmKBKWY+8Zbz+nLXWNWgXqsXPvxSfm5qsAXDg4yu3iLn7Gzq3Jv4t3XceQxpSLQFWZelnmztldnN43wvmDoxyeGGLvtlyb0z+Pt69jSItJBfJBmHpZXnG+Gtq/ejcMhtSBCuQjYWqmzOyHFD77oZo63WC87erbudzTGAMwXfrM2y81nr+rIGw83nb90XQyh9Ccb8/e/CAxCF3aYOZgaB4zYDSffvKvN+ANz+NefXvg4KykbmabDXU30/yOguKbyHYnNzKuwUnmhPxpF3Ok19UsM2r6BEpB6n7NpPFU6smpuLpoqCgZFdCKBDC3MDKmntNSVEuu/AYecjifoa3JogAAAABJRU5ErkJggg=='
unchecked = b'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'
main()

View file

@ -1,103 +0,0 @@
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
"""
'''
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):
self.layout = [ [sg.Text('My layout')],
[sg.Input(key='-IN-')],
[sg.Button('Go'), sg.Button('Exit')] ]
self.window = sg.Window('My new window', self.layout)
def run(self):
while True: # Event Loop
self.event, self.values = self.window.read()
if self.event in (sg.WIN_CLOSED, 'Exit'):
break
if self.event == 'Go':
self.button_go()
self.window.close()
def button_go(self):
sg.popup('Go button clicked', 'Input value:', self.values['-IN-'])
# Create the class
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,24 +0,0 @@
"""
Demo_Close_Attempted_Event
Catches if a window close was tried by user (click "X") and confirms with a popup.
Requires PySimpleGUI 4.33.0 and later
Copyright 2021 PySimpleGUI Inc.
"""
import PySimpleGUI as sg
layout = [[sg.Text('Close confirmation demo')],
[sg.Text('Try closing window with the "X"')],
[sg.Button('Go'), sg.Button('Exit')]]
window = sg.Window('Window Title', layout, enable_close_attempted_event=True)
while True:
event, values = window.read()
print(event, values)
if (event == sg.WINDOW_CLOSE_ATTEMPTED_EVENT or event == 'Exit') and sg.popup_yes_no('Do you really want to exit?') == 'Yes':
break
window.close()

1728
DemoPrograms/Demo_Color.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,719 +0,0 @@
import PySimpleGUI as sg
def popup_color_chooser(look_and_feel=None):
"""
:return: Any(str, None) Returns hex string of color chosen or None if nothing was chosen
"""
color_map = {
'alice blue': '#F0F8FF',
'AliceBlue': '#F0F8FF',
'antique white': '#FAEBD7',
'AntiqueWhite': '#FAEBD7',
'AntiqueWhite1': '#FFEFDB',
'AntiqueWhite2': '#EEDFCC',
'AntiqueWhite3': '#CDC0B0',
'AntiqueWhite4': '#8B8378',
'aquamarine': '#7FFFD4',
'aquamarine1': '#7FFFD4',
'aquamarine2': '#76EEC6',
'aquamarine3': '#66CDAA',
'aquamarine4': '#458B74',
'azure': '#F0FFFF',
'azure1': '#F0FFFF',
'azure2': '#E0EEEE',
'azure3': '#C1CDCD',
'azure4': '#838B8B',
'beige': '#F5F5DC',
'bisque': '#FFE4C4',
'bisque1': '#FFE4C4',
'bisque2': '#EED5B7',
'bisque3': '#CDB79E',
'bisque4': '#8B7D6B',
'black': '#000000',
'blanched almond': '#FFEBCD',
'BlanchedAlmond': '#FFEBCD',
'blue': '#0000FF',
'blue violet': '#8A2BE2',
'blue1': '#0000FF',
'blue2': '#0000EE',
'blue3': '#0000CD',
'blue4': '#00008B',
'BlueViolet': '#8A2BE2',
'brown': '#A52A2A',
'brown1': '#FF4040',
'brown2': '#EE3B3B',
'brown3': '#CD3333',
'brown4': '#8B2323',
'burlywood': '#DEB887',
'burlywood1': '#FFD39B',
'burlywood2': '#EEC591',
'burlywood3': '#CDAA7D',
'burlywood4': '#8B7355',
'cadet blue': '#5F9EA0',
'CadetBlue': '#5F9EA0',
'CadetBlue1': '#98F5FF',
'CadetBlue2': '#8EE5EE',
'CadetBlue3': '#7AC5CD',
'CadetBlue4': '#53868B',
'chartreuse': '#7FFF00',
'chartreuse1': '#7FFF00',
'chartreuse2': '#76EE00',
'chartreuse3': '#66CD00',
'chartreuse4': '#458B00',
'chocolate': '#D2691E',
'chocolate1': '#FF7F24',
'chocolate2': '#EE7621',
'chocolate3': '#CD661D',
'chocolate4': '#8B4513',
'coral': '#FF7F50',
'coral1': '#FF7256',
'coral2': '#EE6A50',
'coral3': '#CD5B45',
'coral4': '#8B3E2F',
'cornflower blue': '#6495ED',
'CornflowerBlue': '#6495ED',
'cornsilk': '#FFF8DC',
'cornsilk1': '#FFF8DC',
'cornsilk2': '#EEE8CD',
'cornsilk3': '#CDC8B1',
'cornsilk4': '#8B8878',
'cyan': '#00FFFF',
'cyan1': '#00FFFF',
'cyan2': '#00EEEE',
'cyan3': '#00CDCD',
'cyan4': '#008B8B',
'dark blue': '#00008B',
'dark cyan': '#008B8B',
'dark goldenrod': '#B8860B',
'dark gray': '#A9A9A9',
'dark green': '#006400',
'dark grey': '#A9A9A9',
'dark khaki': '#BDB76B',
'dark magenta': '#8B008B',
'dark olive green': '#556B2F',
'dark orange': '#FF8C00',
'dark orchid': '#9932CC',
'dark red': '#8B0000',
'dark salmon': '#E9967A',
'dark sea green': '#8FBC8F',
'dark slate blue': '#483D8B',
'dark slate gray': '#2F4F4F',
'dark slate grey': '#2F4F4F',
'dark turquoise': '#00CED1',
'dark violet': '#9400D3',
'DarkBlue': '#00008B',
'DarkCyan': '#008B8B',
'DarkGoldenrod': '#B8860B',
'DarkGoldenrod1': '#FFB90F',
'DarkGoldenrod2': '#EEAD0E',
'DarkGoldenrod3': '#CD950C',
'DarkGoldenrod4': '#8B6508',
'DarkGray': '#A9A9A9',
'DarkGreen': '#006400',
'DarkGrey': '#A9A9A9',
'DarkKhaki': '#BDB76B',
'DarkMagenta': '#8B008B',
'DarkOliveGreen': '#556B2F',
'DarkOliveGreen1': '#CAFF70',
'DarkOliveGreen2': '#BCEE68',
'DarkOliveGreen3': '#A2CD5A',
'DarkOliveGreen4': '#6E8B3D',
'DarkOrange': '#FF8C00',
'DarkOrange1': '#FF7F00',
'DarkOrange2': '#EE7600',
'DarkOrange3': '#CD6600',
'DarkOrange4': '#8B4500',
'DarkOrchid': '#9932CC',
'DarkOrchid1': '#BF3EFF',
'DarkOrchid2': '#B23AEE',
'DarkOrchid3': '#9A32CD',
'DarkOrchid4': '#68228B',
'DarkRed': '#8B0000',
'DarkSalmon': '#E9967A',
'DarkSeaGreen': '#8FBC8F',
'DarkSeaGreen1': '#C1FFC1',
'DarkSeaGreen2': '#B4EEB4',
'DarkSeaGreen3': '#9BCD9B',
'DarkSeaGreen4': '#698B69',
'DarkSlateBlue': '#483D8B',
'DarkSlateGray': '#2F4F4F',
'DarkSlateGray1': '#97FFFF',
'DarkSlateGray2': '#8DEEEE',
'DarkSlateGray3': '#79CDCD',
'DarkSlateGray4': '#528B8B',
'DarkSlateGrey': '#2F4F4F',
'DarkTurquoise': '#00CED1',
'DarkViolet': '#9400D3',
'deep pink': '#FF1493',
'deep sky blue': '#00BFFF',
'DeepPink': '#FF1493',
'DeepPink1': '#FF1493',
'DeepPink2': '#EE1289',
'DeepPink3': '#CD1076',
'DeepPink4': '#8B0A50',
'DeepSkyBlue': '#00BFFF',
'DeepSkyBlue1': '#00BFFF',
'DeepSkyBlue2': '#00B2EE',
'DeepSkyBlue3': '#009ACD',
'DeepSkyBlue4': '#00688B',
'dim gray': '#696969',
'dim grey': '#696969',
'DimGray': '#696969',
'DimGrey': '#696969',
'dodger blue': '#1E90FF',
'DodgerBlue': '#1E90FF',
'DodgerBlue1': '#1E90FF',
'DodgerBlue2': '#1C86EE',
'DodgerBlue3': '#1874CD',
'DodgerBlue4': '#104E8B',
'firebrick': '#B22222',
'firebrick1': '#FF3030',
'firebrick2': '#EE2C2C',
'firebrick3': '#CD2626',
'firebrick4': '#8B1A1A',
'floral white': '#FFFAF0',
'FloralWhite': '#FFFAF0',
'forest green': '#228B22',
'ForestGreen': '#228B22',
'gainsboro': '#DCDCDC',
'ghost white': '#F8F8FF',
'GhostWhite': '#F8F8FF',
'gold': '#FFD700',
'gold1': '#FFD700',
'gold2': '#EEC900',
'gold3': '#CDAD00',
'gold4': '#8B7500',
'goldenrod': '#DAA520',
'goldenrod1': '#FFC125',
'goldenrod2': '#EEB422',
'goldenrod3': '#CD9B1D',
'goldenrod4': '#8B6914',
'green': '#00FF00',
'green yellow': '#ADFF2F',
'green1': '#00FF00',
'green2': '#00EE00',
'green3': '#00CD00',
'green4': '#008B00',
'GreenYellow': '#ADFF2F',
'grey': '#BEBEBE',
'grey0': '#000000',
'grey1': '#030303',
'grey2': '#050505',
'grey3': '#080808',
'grey4': '#0A0A0A',
'grey5': '#0D0D0D',
'grey6': '#0F0F0F',
'grey7': '#121212',
'grey8': '#141414',
'grey9': '#171717',
'grey10': '#1A1A1A',
'grey11': '#1C1C1C',
'grey12': '#1F1F1F',
'grey13': '#212121',
'grey14': '#242424',
'grey15': '#262626',
'grey16': '#292929',
'grey17': '#2B2B2B',
'grey18': '#2E2E2E',
'grey19': '#303030',
'grey20': '#333333',
'grey21': '#363636',
'grey22': '#383838',
'grey23': '#3B3B3B',
'grey24': '#3D3D3D',
'grey25': '#404040',
'grey26': '#424242',
'grey27': '#454545',
'grey28': '#474747',
'grey29': '#4A4A4A',
'grey30': '#4D4D4D',
'grey31': '#4F4F4F',
'grey32': '#525252',
'grey33': '#545454',
'grey34': '#575757',
'grey35': '#595959',
'grey36': '#5C5C5C',
'grey37': '#5E5E5E',
'grey38': '#616161',
'grey39': '#636363',
'grey40': '#666666',
'grey41': '#696969',
'grey42': '#6B6B6B',
'grey43': '#6E6E6E',
'grey44': '#707070',
'grey45': '#737373',
'grey46': '#757575',
'grey47': '#787878',
'grey48': '#7A7A7A',
'grey49': '#7D7D7D',
'grey50': '#7F7F7F',
'grey51': '#828282',
'grey52': '#858585',
'grey53': '#878787',
'grey54': '#8A8A8A',
'grey55': '#8C8C8C',
'grey56': '#8F8F8F',
'grey57': '#919191',
'grey58': '#949494',
'grey59': '#969696',
'grey60': '#999999',
'grey61': '#9C9C9C',
'grey62': '#9E9E9E',
'grey63': '#A1A1A1',
'grey64': '#A3A3A3',
'grey65': '#A6A6A6',
'grey66': '#A8A8A8',
'grey67': '#ABABAB',
'grey68': '#ADADAD',
'grey69': '#B0B0B0',
'grey70': '#B3B3B3',
'grey71': '#B5B5B5',
'grey72': '#B8B8B8',
'grey73': '#BABABA',
'grey74': '#BDBDBD',
'grey75': '#BFBFBF',
'grey76': '#C2C2C2',
'grey77': '#C4C4C4',
'grey78': '#C7C7C7',
'grey79': '#C9C9C9',
'grey80': '#CCCCCC',
'grey81': '#CFCFCF',
'grey82': '#D1D1D1',
'grey83': '#D4D4D4',
'grey84': '#D6D6D6',
'grey85': '#D9D9D9',
'grey86': '#DBDBDB',
'grey87': '#DEDEDE',
'grey88': '#E0E0E0',
'grey89': '#E3E3E3',
'grey90': '#E5E5E5',
'grey91': '#E8E8E8',
'grey92': '#EBEBEB',
'grey93': '#EDEDED',
'grey94': '#F0F0F0',
'grey95': '#F2F2F2',
'grey96': '#F5F5F5',
'grey97': '#F7F7F7',
'grey98': '#FAFAFA',
'grey99': '#FCFCFC',
'grey100': '#FFFFFF',
'honeydew': '#F0FFF0',
'honeydew1': '#F0FFF0',
'honeydew2': '#E0EEE0',
'honeydew3': '#C1CDC1',
'honeydew4': '#838B83',
'hot pink': '#FF69B4',
'HotPink': '#FF69B4',
'HotPink1': '#FF6EB4',
'HotPink2': '#EE6AA7',
'HotPink3': '#CD6090',
'HotPink4': '#8B3A62',
'indian red': '#CD5C5C',
'IndianRed': '#CD5C5C',
'IndianRed1': '#FF6A6A',
'IndianRed2': '#EE6363',
'IndianRed3': '#CD5555',
'IndianRed4': '#8B3A3A',
'ivory': '#FFFFF0',
'ivory1': '#FFFFF0',
'ivory2': '#EEEEE0',
'ivory3': '#CDCDC1',
'ivory4': '#8B8B83',
'khaki': '#F0E68C',
'khaki1': '#FFF68F',
'khaki2': '#EEE685',
'khaki3': '#CDC673',
'khaki4': '#8B864E',
'lavender': '#E6E6FA',
'lavender blush': '#FFF0F5',
'LavenderBlush': '#FFF0F5',
'LavenderBlush1': '#FFF0F5',
'LavenderBlush2': '#EEE0E5',
'LavenderBlush3': '#CDC1C5',
'LavenderBlush4': '#8B8386',
'lawn green': '#7CFC00',
'LawnGreen': '#7CFC00',
'lemon chiffon': '#FFFACD',
'LemonChiffon': '#FFFACD',
'LemonChiffon1': '#FFFACD',
'LemonChiffon2': '#EEE9BF',
'LemonChiffon3': '#CDC9A5',
'LemonChiffon4': '#8B8970',
'light blue': '#ADD8E6',
'light coral': '#F08080',
'light cyan': '#E0FFFF',
'light goldenrod': '#EEDD82',
'light goldenrod yellow': '#FAFAD2',
'light gray': '#D3D3D3',
'light green': '#90EE90',
'light grey': '#D3D3D3',
'light pink': '#FFB6C1',
'light salmon': '#FFA07A',
'light sea green': '#20B2AA',
'light sky blue': '#87CEFA',
'light slate blue': '#8470FF',
'light slate gray': '#778899',
'light slate grey': '#778899',
'light steel blue': '#B0C4DE',
'light yellow': '#FFFFE0',
'LightBlue': '#ADD8E6',
'LightBlue1': '#BFEFFF',
'LightBlue2': '#B2DFEE',
'LightBlue3': '#9AC0CD',
'LightBlue4': '#68838B',
'LightCoral': '#F08080',
'LightCyan': '#E0FFFF',
'LightCyan1': '#E0FFFF',
'LightCyan2': '#D1EEEE',
'LightCyan3': '#B4CDCD',
'LightCyan4': '#7A8B8B',
'LightGoldenrod': '#EEDD82',
'LightGoldenrod1': '#FFEC8B',
'LightGoldenrod2': '#EEDC82',
'LightGoldenrod3': '#CDBE70',
'LightGoldenrod4': '#8B814C',
'LightGoldenrodYellow': '#FAFAD2',
'LightGray': '#D3D3D3',
'LightGreen': '#90EE90',
'LightGrey': '#D3D3D3',
'LightPink': '#FFB6C1',
'LightPink1': '#FFAEB9',
'LightPink2': '#EEA2AD',
'LightPink3': '#CD8C95',
'LightPink4': '#8B5F65',
'LightSalmon': '#FFA07A',
'LightSalmon1': '#FFA07A',
'LightSalmon2': '#EE9572',
'LightSalmon3': '#CD8162',
'LightSalmon4': '#8B5742',
'LightSeaGreen': '#20B2AA',
'LightSkyBlue': '#87CEFA',
'LightSkyBlue1': '#B0E2FF',
'LightSkyBlue2': '#A4D3EE',
'LightSkyBlue3': '#8DB6CD',
'LightSkyBlue4': '#607B8B',
'LightSlateBlue': '#8470FF',
'LightSlateGray': '#778899',
'LightSlateGrey': '#778899',
'LightSteelBlue': '#B0C4DE',
'LightSteelBlue1': '#CAE1FF',
'LightSteelBlue2': '#BCD2EE',
'LightSteelBlue3': '#A2B5CD',
'LightSteelBlue4': '#6E7B8B',
'LightYellow': '#FFFFE0',
'LightYellow1': '#FFFFE0',
'LightYellow2': '#EEEED1',
'LightYellow3': '#CDCDB4',
'LightYellow4': '#8B8B7A',
'lime green': '#32CD32',
'LimeGreen': '#32CD32',
'linen': '#FAF0E6',
'magenta': '#FF00FF',
'magenta1': '#FF00FF',
'magenta2': '#EE00EE',
'magenta3': '#CD00CD',
'magenta4': '#8B008B',
'maroon': '#B03060',
'maroon1': '#FF34B3',
'maroon2': '#EE30A7',
'maroon3': '#CD2990',
'maroon4': '#8B1C62',
'medium aquamarine': '#66CDAA',
'medium blue': '#0000CD',
'medium orchid': '#BA55D3',
'medium purple': '#9370DB',
'medium sea green': '#3CB371',
'medium slate blue': '#7B68EE',
'medium spring green': '#00FA9A',
'medium turquoise': '#48D1CC',
'medium violet red': '#C71585',
'MediumAquamarine': '#66CDAA',
'MediumBlue': '#0000CD',
'MediumOrchid': '#BA55D3',
'MediumOrchid1': '#E066FF',
'MediumOrchid2': '#D15FEE',
'MediumOrchid3': '#B452CD',
'MediumOrchid4': '#7A378B',
'MediumPurple': '#9370DB',
'MediumPurple1': '#AB82FF',
'MediumPurple2': '#9F79EE',
'MediumPurple3': '#8968CD',
'MediumPurple4': '#5D478B',
'MediumSeaGreen': '#3CB371',
'MediumSlateBlue': '#7B68EE',
'MediumSpringGreen': '#00FA9A',
'MediumTurquoise': '#48D1CC',
'MediumVioletRed': '#C71585',
'midnight blue': '#191970',
'MidnightBlue': '#191970',
'mint cream': '#F5FFFA',
'MintCream': '#F5FFFA',
'misty rose': '#FFE4E1',
'MistyRose': '#FFE4E1',
'MistyRose1': '#FFE4E1',
'MistyRose2': '#EED5D2',
'MistyRose3': '#CDB7B5',
'MistyRose4': '#8B7D7B',
'moccasin': '#FFE4B5',
'navajo white': '#FFDEAD',
'NavajoWhite': '#FFDEAD',
'NavajoWhite1': '#FFDEAD',
'NavajoWhite2': '#EECFA1',
'NavajoWhite3': '#CDB38B',
'NavajoWhite4': '#8B795E',
'navy': '#000080',
'navy blue': '#000080',
'NavyBlue': '#000080',
'old lace': '#FDF5E6',
'OldLace': '#FDF5E6',
'olive drab': '#6B8E23',
'OliveDrab': '#6B8E23',
'OliveDrab1': '#C0FF3E',
'OliveDrab2': '#B3EE3A',
'OliveDrab3': '#9ACD32',
'OliveDrab4': '#698B22',
'orange': '#FFA500',
'orange red': '#FF4500',
'orange1': '#FFA500',
'orange2': '#EE9A00',
'orange3': '#CD8500',
'orange4': '#8B5A00',
'OrangeRed': '#FF4500',
'OrangeRed1': '#FF4500',
'OrangeRed2': '#EE4000',
'OrangeRed3': '#CD3700',
'OrangeRed4': '#8B2500',
'orchid': '#DA70D6',
'orchid1': '#FF83FA',
'orchid2': '#EE7AE9',
'orchid3': '#CD69C9',
'orchid4': '#8B4789',
'pale goldenrod': '#EEE8AA',
'pale green': '#98FB98',
'pale turquoise': '#AFEEEE',
'pale violet red': '#DB7093',
'PaleGoldenrod': '#EEE8AA',
'PaleGreen': '#98FB98',
'PaleGreen1': '#9AFF9A',
'PaleGreen2': '#90EE90',
'PaleGreen3': '#7CCD7C',
'PaleGreen4': '#548B54',
'PaleTurquoise': '#AFEEEE',
'PaleTurquoise1': '#BBFFFF',
'PaleTurquoise2': '#AEEEEE',
'PaleTurquoise3': '#96CDCD',
'PaleTurquoise4': '#668B8B',
'PaleVioletRed': '#DB7093',
'PaleVioletRed1': '#FF82AB',
'PaleVioletRed2': '#EE799F',
'PaleVioletRed3': '#CD687F',
'PaleVioletRed4': '#8B475D',
'papaya whip': '#FFEFD5',
'PapayaWhip': '#FFEFD5',
'peach puff': '#FFDAB9',
'PeachPuff': '#FFDAB9',
'PeachPuff1': '#FFDAB9',
'PeachPuff2': '#EECBAD',
'PeachPuff3': '#CDAF95',
'PeachPuff4': '#8B7765',
'peru': '#CD853F',
'pink': '#FFC0CB',
'pink1': '#FFB5C5',
'pink2': '#EEA9B8',
'pink3': '#CD919E',
'pink4': '#8B636C',
'plum': '#DDA0DD',
'plum1': '#FFBBFF',
'plum2': '#EEAEEE',
'plum3': '#CD96CD',
'plum4': '#8B668B',
'powder blue': '#B0E0E6',
'PowderBlue': '#B0E0E6',
'purple': '#A020F0',
'purple1': '#9B30FF',
'purple2': '#912CEE',
'purple3': '#7D26CD',
'purple4': '#551A8B',
'red': '#FF0000',
'red1': '#FF0000',
'red2': '#EE0000',
'red3': '#CD0000',
'red4': '#8B0000',
'rosy brown': '#BC8F8F',
'RosyBrown': '#BC8F8F',
'RosyBrown1': '#FFC1C1',
'RosyBrown2': '#EEB4B4',
'RosyBrown3': '#CD9B9B',
'RosyBrown4': '#8B6969',
'royal blue': '#4169E1',
'RoyalBlue': '#4169E1',
'RoyalBlue1': '#4876FF',
'RoyalBlue2': '#436EEE',
'RoyalBlue3': '#3A5FCD',
'RoyalBlue4': '#27408B',
'saddle brown': '#8B4513',
'SaddleBrown': '#8B4513',
'salmon': '#FA8072',
'salmon1': '#FF8C69',
'salmon2': '#EE8262',
'salmon3': '#CD7054',
'salmon4': '#8B4C39',
'sandy brown': '#F4A460',
'SandyBrown': '#F4A460',
'sea green': '#2E8B57',
'SeaGreen': '#2E8B57',
'SeaGreen1': '#54FF9F',
'SeaGreen2': '#4EEE94',
'SeaGreen3': '#43CD80',
'SeaGreen4': '#2E8B57',
'seashell': '#FFF5EE',
'seashell1': '#FFF5EE',
'seashell2': '#EEE5DE',
'seashell3': '#CDC5BF',
'seashell4': '#8B8682',
'sienna': '#A0522D',
'sienna1': '#FF8247',
'sienna2': '#EE7942',
'sienna3': '#CD6839',
'sienna4': '#8B4726',
'sky blue': '#87CEEB',
'SkyBlue': '#87CEEB',
'SkyBlue1': '#87CEFF',
'SkyBlue2': '#7EC0EE',
'SkyBlue3': '#6CA6CD',
'SkyBlue4': '#4A708B',
'slate blue': '#6A5ACD',
'slate gray': '#708090',
'slate grey': '#708090',
'SlateBlue': '#6A5ACD',
'SlateBlue1': '#836FFF',
'SlateBlue2': '#7A67EE',
'SlateBlue3': '#6959CD',
'SlateBlue4': '#473C8B',
'SlateGray': '#708090',
'SlateGray1': '#C6E2FF',
'SlateGray2': '#B9D3EE',
'SlateGray3': '#9FB6CD',
'SlateGray4': '#6C7B8B',
'SlateGrey': '#708090',
'snow': '#FFFAFA',
'snow1': '#FFFAFA',
'snow2': '#EEE9E9',
'snow3': '#CDC9C9',
'snow4': '#8B8989',
'spring green': '#00FF7F',
'SpringGreen': '#00FF7F',
'SpringGreen1': '#00FF7F',
'SpringGreen2': '#00EE76',
'SpringGreen3': '#00CD66',
'SpringGreen4': '#008B45',
'steel blue': '#4682B4',
'SteelBlue': '#4682B4',
'SteelBlue1': '#63B8FF',
'SteelBlue2': '#5CACEE',
'SteelBlue3': '#4F94CD',
'SteelBlue4': '#36648B',
'tan': '#D2B48C',
'tan1': '#FFA54F',
'tan2': '#EE9A49',
'tan3': '#CD853F',
'tan4': '#8B5A2B',
'thistle': '#D8BFD8',
'thistle1': '#FFE1FF',
'thistle2': '#EED2EE',
'thistle3': '#CDB5CD',
'thistle4': '#8B7B8B',
'tomato': '#FF6347',
'tomato1': '#FF6347',
'tomato2': '#EE5C42',
'tomato3': '#CD4F39',
'tomato4': '#8B3626',
'turquoise': '#40E0D0',
'turquoise1': '#00F5FF',
'turquoise2': '#00E5EE',
'turquoise3': '#00C5CD',
'turquoise4': '#00868B',
'violet': '#EE82EE',
'violet red': '#D02090',
'VioletRed': '#D02090',
'VioletRed1': '#FF3E96',
'VioletRed2': '#EE3A8C',
'VioletRed3': '#CD3278',
'VioletRed4': '#8B2252',
'wheat': '#F5DEB3',
'wheat1': '#FFE7BA',
'wheat2': '#EED8AE',
'wheat3': '#CDBA96',
'wheat4': '#8B7E66',
'white': '#FFFFFF',
'white smoke': '#F5F5F5',
'WhiteSmoke': '#F5F5F5',
'yellow': '#FFFF00',
'yellow green': '#9ACD32',
'yellow1': '#FFFF00',
'yellow2': '#EEEE00',
'yellow3': '#CDCD00',
'yellow4': '#8B8B00',
'YellowGreen': '#9ACD32',
}
old_look_and_feel = None
if look_and_feel is not None:
old_look_and_feel = sg.CURRENT_LOOK_AND_FEEL
sg.theme(look_and_feel)
button_size = (1, 1)
# button_size = (None,None) # for very compact buttons
def ColorButton(color):
"""
A User Defined Element - returns a Button that configured in a certain way.
:param color: Tuple[str, str] ( color name, hex string)
:return: sg.Button object
"""
return sg.B(button_color=('white', color[1]), pad=(0, 0), size=button_size, key=color, tooltip=f'{color[0]}:{color[1]}', border_width=0)
num_colors = len(list(color_map.keys()))
row_len = 40
grid = [[ColorButton(list(color_map.items())[c + j * row_len]) for c in range(0, row_len)] for j in range(0, num_colors // row_len)]
grid += [[ColorButton(list(color_map.items())[c + num_colors - num_colors % row_len]) for c in range(0, num_colors % row_len)]]
layout = [[sg.Text('Pick a color', font='Def 18')]] + grid + \
[[sg.Button('OK'), sg.T(size=(30, 1), key='-OUT-')]]
window = sg.Window('Window Title', layout, no_titlebar=True, grab_anywhere=True, keep_on_top=True, use_ttk_buttons=True)
color_chosen = None
while True: # Event Loop
event, values = window.read()
if event in (sg.WIN_CLOSED, 'OK'):
if event == sg.WIN_CLOSED:
color_chosen = None
break
window['-OUT-'](f'You chose {event[0]} : {event[1]}')
color_chosen = event[1]
window.close()
if old_look_and_feel is not None:
sg.theme(old_look_and_feel)
return color_chosen
if __name__ == '__main__':
sg.theme('Light Brown 4')
layout = [[sg.In(key='-CHOICE-'), sg.B('Color Picker')],
[sg.Ok(), sg.Cancel()]]
window = sg.Window('My application', layout)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Cancel'):
break
if event.startswith('Color'):
window.hide()
color_chosen = popup_color_chooser('Dark Blue 3')
window['-CHOICE-'].update(color_chosen)
window.un_hide()
else:
print(f'The current look and feel = {sg.CURRENT_LOOK_AND_FEEL}')

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