Renamed demo files, new demo of tabbed forms
Changed filenames to remove spaces so will be easier to work with on Linux.
This commit is contained in:
parent
13d99dcd75
commit
2109bdbc97
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
def GetFilesToCompare():
|
||||||
|
with sg.FlexForm('File Compare', auto_size_text=True) as form:
|
||||||
|
form_rows = [[sg.Text('Enter 2 files to comare')],
|
||||||
|
[sg.Text('File 1', size=(15, 1)), sg.InputText(), sg.FileBrowse()],
|
||||||
|
[sg.Text('File 2', size=(15, 1)), sg.InputText(), sg.FileBrowse()],
|
||||||
|
[sg.Submit(), sg.Cancel()]]
|
||||||
|
rc = form.LayoutAndShow(form_rows)
|
||||||
|
return rc
|
||||||
|
|
||||||
|
def main():
|
||||||
|
button, (f1, f2) = GetFilesToCompare()
|
||||||
|
if any((button != 'Submit', f1 =='', f2 == '')):
|
||||||
|
sg.MsgBoxError('Operation cancelled')
|
||||||
|
exit(69)
|
||||||
|
|
||||||
|
with open(f1, 'rb') as file1:
|
||||||
|
with open(f2, 'rb') as file2:
|
||||||
|
a = file1.read()
|
||||||
|
b = file2.read()
|
||||||
|
|
||||||
|
for i, x in enumerate(a):
|
||||||
|
if x != b[i]:
|
||||||
|
sg.MsgBox('Compare results for files', f1, f2, '**** Mismatch at offset {} ****'.format(i))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if len(a) == len(b):
|
||||||
|
sg.MsgBox('**** The files are IDENTICAL ****')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!Python 3
|
||||||
|
import hashlib
|
||||||
|
import PySimpleGUI as SG
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
# DisplayHash #
|
||||||
|
# A PySimpleGUI demo app that displays SHA1 hash for user browsed file #
|
||||||
|
# Useful and a recipe for GUI success #
|
||||||
|
#########################################################################
|
||||||
|
|
||||||
|
# ====____====____==== FUNCTION compute_hash_for_file(filename) ====____====____==== #
|
||||||
|
# Reads a file, computes the Hash #
|
||||||
|
# ---------------------------------------------------------------------------------- #
|
||||||
|
def compute_sha1_hash_for_file(filename):
|
||||||
|
try:
|
||||||
|
x = open(filename, "rb").read()
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
m = hashlib.sha1()
|
||||||
|
m.update(x)
|
||||||
|
f_sha = m.hexdigest()
|
||||||
|
|
||||||
|
return f_sha
|
||||||
|
|
||||||
|
|
||||||
|
# ====____====____==== FUNCTION compute_hash_for_file(filename) ====____====____==== #
|
||||||
|
# Reads a file, computes the Hash #
|
||||||
|
# ---------------------------------------------------------------------------------- #
|
||||||
|
def compute_sha256_hash_for_file(filename):
|
||||||
|
try:
|
||||||
|
f = open(filename, "rb")
|
||||||
|
x = f.read()
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
m = hashlib.sha256()
|
||||||
|
m.update(x)
|
||||||
|
f_sha = m.hexdigest()
|
||||||
|
|
||||||
|
return f_sha
|
||||||
|
|
||||||
|
|
||||||
|
# ====____====____==== Uses A GooeyGUI GUI ====____====____== #
|
||||||
|
# Get the filename, display the hash, dirt simple all around #
|
||||||
|
# ----------------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Compute and display SHA1 hash #
|
||||||
|
# Builds and displays the form using the most basic building blocks #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
def HashManuallyBuiltGUI():
|
||||||
|
# ------- Form design ------- #
|
||||||
|
with SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True) as form:
|
||||||
|
form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')],
|
||||||
|
[SG.InputText(), SG.FileBrowse()],
|
||||||
|
[SG.Submit(), SG.Cancel()]]
|
||||||
|
(button, (source_filename, )) = form.LayoutAndShow(form_rows)
|
||||||
|
|
||||||
|
if button == 'Submit':
|
||||||
|
if source_filename != '':
|
||||||
|
hash_sha1 = compute_sha1_hash_for_file(source_filename).upper()
|
||||||
|
hash_sha256 = compute_sha256_hash_for_file(source_filename).upper()
|
||||||
|
SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, line_width=75)
|
||||||
|
else: SG.MsgBoxError('Display A Hash in PySimpleGUI', 'Illegal filename')
|
||||||
|
else:
|
||||||
|
SG.MsgBoxError('Display A Hash in PySimpleGUI', '* Cancelled *')
|
||||||
|
|
||||||
|
def HashManuallyBuiltGUINonContext():
|
||||||
|
# ------- Form design ------- #
|
||||||
|
form = SG.FlexForm('SHA-1 & 256 Hash', auto_size_text=True)
|
||||||
|
form_rows = [[SG.Text('SHA-1 and SHA-256 Hashes for the file')],
|
||||||
|
[SG.InputText(), SG.FileBrowse()],
|
||||||
|
[SG.Submit(), SG.Cancel()]]
|
||||||
|
button, (source_filename, ) = form.LayoutAndShow(form_rows)
|
||||||
|
|
||||||
|
if button == 'Submit':
|
||||||
|
if source_filename != '':
|
||||||
|
hash_sha1 = compute_sha1_hash_for_file(source_filename).upper()
|
||||||
|
hash_sha256 = compute_sha256_hash_for_file(source_filename).upper()
|
||||||
|
SG.MsgBox( 'Display A Hash in PySimpleGUI', 'The SHA-1 Hash for the file\n', source_filename, hash_sha1, 'SHA-256 is', hash_sha256, line_width=75)
|
||||||
|
else: SG.MsgBoxError('Display A Hash in PySimpleGUI', 'Illegal filename')
|
||||||
|
else:
|
||||||
|
SG.MsgBoxError('Display A Hash in PySimpleGUI', '* Cancelled *')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Compute and display SHA1 hash #
|
||||||
|
# This one cheats and uses the higher-level Get A File pre-made func #
|
||||||
|
# Hey, it's a really common operation so why not? #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
def HashMostCompactGUI():
|
||||||
|
# ------- INPUT GUI portion ------- #
|
||||||
|
|
||||||
|
rc, source_filename = SG.GetFileBox('Display A Hash Using PySimpleGUI',
|
||||||
|
'Display a Hash code for file of your choice')
|
||||||
|
|
||||||
|
# ------- OUTPUT GUI results portion ------- #
|
||||||
|
if rc == True:
|
||||||
|
hash = compute_sha1_hash_for_file(source_filename)
|
||||||
|
SG.MsgBox('Display Hash - Compact GUI', 'The SHA-1 Hash for the file\n', source_filename, hash)
|
||||||
|
else:
|
||||||
|
SG.MsgBox('Display Hash - Compact GUI', '* Cancelled *')
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
# Our main calls two GUIs that act identically but use different calls #
|
||||||
|
# ---------------------------------------------------------------------- #
|
||||||
|
def main():
|
||||||
|
HashManuallyBuiltGUINonContext()
|
||||||
|
HashMostCompactGUI()
|
||||||
|
|
||||||
|
|
||||||
|
# ====____====____==== Pseudo-MAIN program ====____====____==== #
|
||||||
|
# This is our main-alike piece of code #
|
||||||
|
# + Starts up the GUI #
|
||||||
|
# + Gets values from GUI #
|
||||||
|
# + Runs DeDupe_folder based on GUI inputs #
|
||||||
|
# ------------------------------------------------------------- #
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,57 @@
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
|
||||||
|
# ====____====____==== FUNCTION DeDuplicate_folder(path) ====____====____==== #
|
||||||
|
# Function to de-duplicate the folder passed in #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
def FindDuplicatesFilesInFolder(path):
|
||||||
|
shatab = []
|
||||||
|
total = 0
|
||||||
|
small_count, dup_count, error_count = 0,0,0
|
||||||
|
pngdir = path
|
||||||
|
if not os.path.exists(path):
|
||||||
|
sg.MsgBox('Duplicate Finder', '** Folder doesn\'t exist***', path)
|
||||||
|
return
|
||||||
|
pngfiles = os.listdir(pngdir)
|
||||||
|
total_files = len(pngfiles)
|
||||||
|
for idx, f in enumerate(pngfiles):
|
||||||
|
if not sg.EasyProgressMeter('Counting Duplicates', idx + 1, total_files, 'Counting Duplicate Files'):
|
||||||
|
break
|
||||||
|
total += 1
|
||||||
|
fname = os.path.join(pngdir, f)
|
||||||
|
if os.path.isdir(fname):
|
||||||
|
continue
|
||||||
|
x = open(fname, "rb").read()
|
||||||
|
|
||||||
|
m = hashlib.sha256()
|
||||||
|
m.update(x)
|
||||||
|
f_sha = m.digest()
|
||||||
|
if f_sha in shatab:
|
||||||
|
# uncomment next line to remove duplicate files
|
||||||
|
# os.remove(fname)
|
||||||
|
dup_count += 1
|
||||||
|
# sg.Print(f'Duplicate file - {f}') # cannot current use sg.Print with Progress Meter
|
||||||
|
continue
|
||||||
|
shatab.append(f_sha)
|
||||||
|
|
||||||
|
msg = f'{total} Files processed\n'\
|
||||||
|
f'{dup_count} Duplicates found\n'
|
||||||
|
sg.MsgBox('Duplicate Finder Ended', msg)
|
||||||
|
|
||||||
|
# ====____====____==== Pseudo-MAIN program ====____====____==== #
|
||||||
|
# This is our main-alike piece of code #
|
||||||
|
# + Starts up the GUI #
|
||||||
|
# + Gets values from GUI #
|
||||||
|
# + Runs DeDupe_folder based on GUI inputs #
|
||||||
|
# ------------------------------------------------------------- #
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
source_folder = None
|
||||||
|
rc, source_folder = sg.GetPathBox('Duplicate Finder - Count number of duplicate files', 'Enter path to folder you wish to find duplicates in')
|
||||||
|
if rc is True and source_folder is not None:
|
||||||
|
FindDuplicatesFilesInFolder(source_folder)
|
||||||
|
else:
|
||||||
|
sg.MsgBoxCancel('Cancelling', '*** Cancelling ***')
|
||||||
|
exit(0)
|
|
@ -0,0 +1,50 @@
|
||||||
|
import PySimpleGUI as gg
|
||||||
|
import time
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# ------- Make a new FlexForm ------- #
|
||||||
|
form = gg.FlexForm('GoodColors', auto_size_text=True, default_element_size=(30,2))
|
||||||
|
form.AddRow(gg.Text('Having trouble picking good colors? Try one of the colors defined by PySimpleGUI'))
|
||||||
|
form.AddRow(gg.Text('Here come the good colors as defined by PySimpleGUI'))
|
||||||
|
|
||||||
|
#===== Show some nice BLUE colors with yellow text ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
text_color = gg.YELLOWS[0]
|
||||||
|
buttons = (gg.SimpleButton(f'BLUES[{j}]\n{c}', button_color=(text_color, c), size=(10,2)) for j, c in enumerate(gg.BLUES))
|
||||||
|
form.AddRow(gg.T('Button Colors Using PySimpleGUI.BLUES'))
|
||||||
|
form.AddRow(*buttons)
|
||||||
|
form.AddRow(gg.Text('_' * 100, size=(65, 1)))
|
||||||
|
|
||||||
|
#===== Show some nice PURPLE colors with yellow text ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
buttons = (gg.SimpleButton(f'PURPLES[{j}]\n{c}', button_color=(text_color, c), size=(10,2)) for j, c in enumerate(gg.PURPLES))
|
||||||
|
form.AddRow(gg.T('Button Colors Using PySimpleGUI.PURPLES'))
|
||||||
|
form.AddRow(*buttons)
|
||||||
|
form.AddRow(gg.Text('_' * 100, size=(65, 1)))
|
||||||
|
|
||||||
|
#===== Show some nice GREEN colors with yellow text ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
buttons = (gg.SimpleButton(f'GREENS[{j}]\n{c}', button_color=(text_color, c), size=(10,2)) for j, c in enumerate(gg.GREENS))
|
||||||
|
form.AddRow(gg.T('Button Colors Using PySimpleGUI.GREENS'))
|
||||||
|
form.AddRow(*buttons)
|
||||||
|
form.AddRow(gg.Text('_' * 100, size=(65, 1)))
|
||||||
|
|
||||||
|
#===== Show some nice TAN colors with yellow text ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
text_color = gg.GREENS[0] # let's use GREEN text on the tan
|
||||||
|
buttons = (gg.SimpleButton(f'TANS[{j}]\n{c}', button_color=(text_color, c), size=(10,2)) for j, c in enumerate(gg.TANS))
|
||||||
|
form.AddRow(gg.T('Button Colors Using PySimpleGUI.TANS'))
|
||||||
|
form.AddRow(*buttons)
|
||||||
|
form.AddRow(gg.Text('_' * 100, size=(65, 1)))
|
||||||
|
|
||||||
|
#===== Show some nice YELLOWS colors with black text ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
text_color = 'black' # let's use black text on the tan
|
||||||
|
buttons = (gg.SimpleButton(f'YELLOWS[{j}]\n{c}', button_color=(text_color, c), size=(10,2)) for j, c in enumerate(gg.YELLOWS))
|
||||||
|
form.AddRow(gg.T('Button Colors Using PySimpleGUI.YELLOWS'))
|
||||||
|
form.AddRow(*buttons)
|
||||||
|
form.AddRow(gg.Text('_' * 100, size=(65, 1)))
|
||||||
|
|
||||||
|
|
||||||
|
#===== Add a click me button for fun and SHOW the form ===== ===== ===== ===== ===== ===== =====#
|
||||||
|
form.AddRow(gg.SimpleButton('Click ME!'))
|
||||||
|
(button, value) = form.Show() # show it!
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,55 @@
|
||||||
|
import PySimpleGUI as SG
|
||||||
|
import subprocess
|
||||||
|
import howdoi
|
||||||
|
|
||||||
|
# Test this command in a dos window if you are having trouble.
|
||||||
|
HOW_DO_I_COMMAND = 'python -m howdoi.howdoi'
|
||||||
|
|
||||||
|
# if you want an icon on your taskbar for this gui, then change this line of code to point to the ICO file
|
||||||
|
DEFAULT_ICON = 'E:\\TheRealMyDocs\\Icons\\QuestionMark.ico'
|
||||||
|
|
||||||
|
def HowDoI():
|
||||||
|
'''
|
||||||
|
Make and show a window (PySimpleGUI form) that takes user input and sends to the HowDoI web oracle
|
||||||
|
Excellent example of 2 GUI concepts
|
||||||
|
1. Output Element that will show text in a scrolled window
|
||||||
|
2. Non-Window-Closing Buttons - These buttons will cause the form to return with the form's values, but doesn't close the form
|
||||||
|
:return: never returns
|
||||||
|
'''
|
||||||
|
# ------- Make a new FlexForm ------- #
|
||||||
|
SG.SetOptions(border_width=1)
|
||||||
|
form = SG.FlexForm('How Do I ??', auto_size_text=True, default_element_size=(30, 2), icon=DEFAULT_ICON)
|
||||||
|
form.AddRow(SG.Text('Ask and your answer will appear here....', size=(40, 1)))
|
||||||
|
form.AddRow(SG.Output(size=(90, 20)))
|
||||||
|
form.AddRow(SG.Multiline(size=(85, 5), enter_submits=True),
|
||||||
|
SG.ReadFormButton('SEND', button_color=(SG.YELLOWS[0], SG.BLUES[0])),
|
||||||
|
SG.SimpleButton('EXIT', button_color=(SG.YELLOWS[0], SG.GREENS[0])))
|
||||||
|
|
||||||
|
# ---===--- Loop taking in user input and using it to query HowDoI --- #
|
||||||
|
while True:
|
||||||
|
(button, value) = form.Read()
|
||||||
|
if button == 'SEND':
|
||||||
|
QueryHowDoI(value[0][:-1]) # send string without carriage return on end
|
||||||
|
else:
|
||||||
|
break # exit button clicked
|
||||||
|
|
||||||
|
exit(69)
|
||||||
|
|
||||||
|
def QueryHowDoI(Query):
|
||||||
|
'''
|
||||||
|
Kicks off a subprocess to send the 'Query' to HowDoI
|
||||||
|
Prints the result, which in this program will route to a gooeyGUI window
|
||||||
|
:param Query: text english question to ask the HowDoI web engine
|
||||||
|
:return: nothing
|
||||||
|
'''
|
||||||
|
howdoi_command = HOW_DO_I_COMMAND
|
||||||
|
t = subprocess.Popen(howdoi_command + ' '+ Query, stdout=subprocess.PIPE)
|
||||||
|
(output, err) = t.communicate()
|
||||||
|
print('You asked: '+ Query)
|
||||||
|
print('_______________________________________')
|
||||||
|
print(output.decode("utf-8") )
|
||||||
|
exit_code = t.wait()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
HowDoI()
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
#
|
||||||
|
# An Async Demonstration of a media player
|
||||||
|
# Uses button images for a super snazzy look
|
||||||
|
# See how it looks here:
|
||||||
|
# https://user-images.githubusercontent.com/13696193/43159403-45c9726e-8f50-11e8-9da0-0d272e20c579.jpg
|
||||||
|
#
|
||||||
|
def MediaPlayerGUI():
|
||||||
|
|
||||||
|
# Images are located in a subfolder in the Demo Media Player.py folder
|
||||||
|
image_pause = './ButtonGraphics/Pause.png'
|
||||||
|
image_restart = './ButtonGraphics/Restart.png'
|
||||||
|
image_next = './ButtonGraphics/Next.png'
|
||||||
|
image_exit = './ButtonGraphics/Exit.png'
|
||||||
|
|
||||||
|
# A text element that will be changed to display messages in the GUI
|
||||||
|
TextElem = sg.Text('', size=(15, 2), font=("Helvetica", 14))
|
||||||
|
|
||||||
|
# Open a form, note that context manager can't be used generally speaking for async forms
|
||||||
|
form = sg.FlexForm('Media File Player', auto_size_text=True, default_element_size=(20, 1),
|
||||||
|
font=("Helvetica", 25))
|
||||||
|
# define layout of the rows
|
||||||
|
layout= [[sg.Text('Media File Player',size=(17,1), font=("Helvetica", 25))],
|
||||||
|
[TextElem],
|
||||||
|
[sg.ReadFormButton('Restart Song', button_color=sg.TRANSPARENT_BUTTON,
|
||||||
|
image_filename=image_restart, image_size=(50, 50), image_subsample=2, border_width=0),
|
||||||
|
sg.Text(' ' * 2),
|
||||||
|
sg.ReadFormButton('Pause', button_color=sg.TRANSPARENT_BUTTON,
|
||||||
|
image_filename=image_pause, image_size=(50, 50), image_subsample=2, border_width=0),
|
||||||
|
sg.Text(' ' * 2),
|
||||||
|
sg.ReadFormButton('Next', button_color=sg.TRANSPARENT_BUTTON,
|
||||||
|
image_filename=image_next, image_size=(50, 50), image_subsample=2, border_width=0),
|
||||||
|
sg.Text(' ' * 2),
|
||||||
|
sg.Text(' ' * 2), sg.SimpleButton('Exit', button_color=sg.TRANSPARENT_BUTTON,
|
||||||
|
image_filename=image_exit, image_size=(50, 50), image_subsample=2, border_width=0)],
|
||||||
|
[sg.Text('_'*30)],
|
||||||
|
[sg.Text(' '*30)],
|
||||||
|
[
|
||||||
|
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)),
|
||||||
|
sg.Text(' ' * 2),
|
||||||
|
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15)),
|
||||||
|
sg.Text(' ' * 8),
|
||||||
|
sg.Slider(range=(-10, 10), default_value=0, size=(10, 20), orientation='vertical', font=("Helvetica", 15))],
|
||||||
|
[sg.Text('Bass', font=("Helvetica", 15), size=(6, 1)),
|
||||||
|
sg.Text('Treble', font=("Helvetica", 15), size=(10, 1)),
|
||||||
|
sg.Text('Volume', font=("Helvetica", 15), size=(7, 1))]
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
# Call the same LayoutAndRead but indicate the form is non-blocking
|
||||||
|
form.LayoutAndRead(layout, non_blocking=True)
|
||||||
|
# Our event loop
|
||||||
|
while(True):
|
||||||
|
# Read the form (this call will not block)
|
||||||
|
button, values = form.ReadNonBlocking()
|
||||||
|
if button == 'Exit':
|
||||||
|
break
|
||||||
|
# If a button was pressed, display it on the GUI by updating the text element
|
||||||
|
if button:
|
||||||
|
TextElem.Update(button)
|
||||||
|
|
||||||
|
MediaPlayerGUI()
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
import time
|
||||||
|
|
||||||
|
def main():
|
||||||
|
StatusOutputExample()
|
||||||
|
|
||||||
|
# form that doen't block
|
||||||
|
def StatusOutputExample_context_manager():
|
||||||
|
with sg.FlexForm('Running Timer', auto_size_text=True) as form:
|
||||||
|
output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20))
|
||||||
|
form_rows = [[sg.Text('Non-blocking GUI with updates')],
|
||||||
|
[output_element],
|
||||||
|
[sg.SimpleButton('Quit')]]
|
||||||
|
|
||||||
|
form.LayoutAndRead(form_rows, non_blocking=True)
|
||||||
|
|
||||||
|
for i in range(1, 1000):
|
||||||
|
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i/100), 60), i%100))
|
||||||
|
button, values = form.ReadNonBlocking()
|
||||||
|
if values is None or button == 'Quit':
|
||||||
|
break
|
||||||
|
time.sleep(.01)
|
||||||
|
else:
|
||||||
|
form.CloseNonBlockingForm()
|
||||||
|
|
||||||
|
|
||||||
|
# form that doen't block
|
||||||
|
def StatusOutputExample():
|
||||||
|
# Make a form, but don't use context manager
|
||||||
|
form = sg.FlexForm('Running Timer', auto_size_text=True)
|
||||||
|
# Create a text element that will be updated with status information on the GUI itself
|
||||||
|
output_element = sg.Text('', size=(8, 2), font=('Helvetica', 20))
|
||||||
|
# Create the rows
|
||||||
|
form_rows = [[sg.Text('Non-blocking GUI with updates')],
|
||||||
|
[output_element],
|
||||||
|
[sg.SimpleButton('Quit')]]
|
||||||
|
# Layout the rows of the form and perform a read. Indicate the form is non-blocking!
|
||||||
|
form.LayoutAndRead(form_rows, non_blocking=True)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Some place later in your code...
|
||||||
|
# You need to perform a ReadNonBlocking on your form every now and then or
|
||||||
|
# else it won't refresh
|
||||||
|
#
|
||||||
|
|
||||||
|
for i in range(1, 1000):
|
||||||
|
output_element.Update('{:02d}:{:02d}.{:02d}'.format(*divmod(int(i / 100), 60), i % 100))
|
||||||
|
button, values = form.ReadNonBlocking()
|
||||||
|
if values is None or button == 'Quit':
|
||||||
|
break
|
||||||
|
time.sleep(.01)
|
||||||
|
else:
|
||||||
|
form.CloseNonBlockingForm()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
main()
|
|
@ -0,0 +1,168 @@
|
||||||
|
import time
|
||||||
|
from random import randint
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
# A simple blocking form. Your best starter-form
|
||||||
|
def SourceDestFolders():
|
||||||
|
with sg.FlexForm('Demo Source / Destination Folders', auto_size_text=True) as form:
|
||||||
|
form_rows = [[sg.Text('Enter the Source and Destination folders')],
|
||||||
|
[sg.Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Source')],
|
||||||
|
[sg.Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Dest'), sg.FolderBrowse()],
|
||||||
|
[sg.Submit(), sg.Cancel()]]
|
||||||
|
|
||||||
|
button, (source, dest) = form.LayoutAndRead(form_rows)
|
||||||
|
if button == 'Submit':
|
||||||
|
sg.MsgBox('Submitted', 'The user entered source:', source, 'Destination folder:', dest, 'Using button', button)
|
||||||
|
else:
|
||||||
|
sg.MsgBoxError('Cancelled', 'User Cancelled')
|
||||||
|
|
||||||
|
# YOUR BEST STARTING POINT
|
||||||
|
# This is a form showing you all of the basic Elements (widgets)
|
||||||
|
# Some have a few of the optional parameters set, but there are more to choose from
|
||||||
|
# You want to use the context manager because it will free up resources when you are finished
|
||||||
|
# Use this especially if you are runningm multi-threaded
|
||||||
|
# Where you free up resources is really important to tkinter
|
||||||
|
def Everything():
|
||||||
|
with sg.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1)) as form:
|
||||||
|
layout = [
|
||||||
|
[sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25), text_color='blue')],
|
||||||
|
[sg.Text('Here is some text.... and a place to enter text')],
|
||||||
|
[sg.InputText()],
|
||||||
|
[sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)],
|
||||||
|
[sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")],
|
||||||
|
[sg.Multiline(default_text='This is the default Text shoulsd you decide not to type anything',
|
||||||
|
scale=(2, 10))],
|
||||||
|
[sg.InputCombo(['Combobox 1', 'Combobox 2'], size=(20, 3)),
|
||||||
|
sg.Slider(range=(1, 100), orientation='h', size=(35, 20), default_value=85)],
|
||||||
|
[sg.Listbox(values=['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(30, 6)),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=25),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=75),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=10),
|
||||||
|
sg.Spin(values=('Spin Box 1', '2','3'), initial_value='Spin Box 1')],
|
||||||
|
[sg.Text('_' * 100, size=(70, 1))],
|
||||||
|
[sg.Text('Choose Source and Destination Folders', size=(35, 1))],
|
||||||
|
[sg.Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Source'), sg.FolderBrowse()],
|
||||||
|
[sg.Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Dest'),
|
||||||
|
sg.FolderBrowse()],
|
||||||
|
[sg.Submit(), sg.Cancel(), sg.SimpleButton('Customized', button_color=('white', 'green'))]
|
||||||
|
]
|
||||||
|
|
||||||
|
button, values = form.LayoutAndRead(layout)
|
||||||
|
|
||||||
|
sg.MsgBox('Title', 'Typical message box', 'The results of the form are a lot of data! Get ready... ', 'The button clicked was "{}"'.format(button), 'The values are', values)
|
||||||
|
|
||||||
|
# Should you decide not to use a context manager, then try this form as your starting point
|
||||||
|
# Be aware that tkinter, which this is based on, is picky about who frees up resources, especially if
|
||||||
|
# you are running multithreaded
|
||||||
|
def Everything_NoContextManager():
|
||||||
|
form = sg.FlexForm('Everything bagel', auto_size_text=True, default_element_size=(40, 1))
|
||||||
|
layout = [[sg.Text('All graphic widgets in one form!', size=(30, 1), font=("Helvetica", 25), text_color='blue')],
|
||||||
|
[sg.Text('Here is some text.... and a place to enter text')],
|
||||||
|
[sg.InputText()],
|
||||||
|
[sg.Checkbox('My first checkbox!'), sg.Checkbox('My second checkbox!', default=True)],
|
||||||
|
[sg.Radio('My first Radio! ', "RADIO1", default=True), sg.Radio('My second Radio!', "RADIO1")],
|
||||||
|
[sg.Multiline(default_text='This is the default Text shoulsd you decide not to type anything', scale=(2, 10))],
|
||||||
|
[sg.InputCombo(['Combobox 1', 'Combobox 2'], size=(20, 3)), sg.Slider(range=(1, 100), orientation='h', size=(35, 20), default_value=85)],
|
||||||
|
[sg.Listbox(values=['Listbox 1', 'Listbox 2', 'Listbox 3'], size=(30, 6)),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=25),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=75),
|
||||||
|
sg.Slider(range=(1, 100), orientation='v', size=(10, 20), default_value=10)],
|
||||||
|
[sg.Text('_' * 100, size=(70, 1))],
|
||||||
|
[sg.Text('Choose Source and Destination Folders', size=(35, 1), text_color='red')],
|
||||||
|
[sg.Text('Source Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Source'), sg.FolderBrowse()],
|
||||||
|
[sg.Text('Destination Folder', size=(15, 1), auto_size_text=False, justification='right'), sg.InputText('Dest'), sg.FolderBrowse()],
|
||||||
|
[sg.Submit(), sg.Cancel(), sg.SimpleButton('Customized', button_color=('white', 'green'))]]
|
||||||
|
|
||||||
|
button, values = form.LayoutAndRead(layout)
|
||||||
|
del(form)
|
||||||
|
|
||||||
|
sg.MsgBox('Title', 'Typical message box', 'Here are the restults! There is one entry per input field ', 'The button clicked was "{}"'.format(button), 'The values are', values)
|
||||||
|
|
||||||
|
def ProgressMeter():
|
||||||
|
for i in range(1,10000):
|
||||||
|
if not sg.EasyProgressMeter('My Meter', i + 1, 10000): break
|
||||||
|
# SG.Print(i)
|
||||||
|
|
||||||
|
# Blocking form that doesn't close
|
||||||
|
def ChatBot():
|
||||||
|
with sg.FlexForm('Chat Window', auto_size_text=True, default_element_size=(30, 2)) as form:
|
||||||
|
layout = [[(sg.Text('This is where standard out is being routed', size=[40, 1]))],
|
||||||
|
[sg.Output(size=(80, 20))],
|
||||||
|
[sg.Multiline(size=(70, 5), enter_submits=True), sg.ReadFormButton('SEND', button_color=(sg.YELLOWS[0], sg.BLUES[0])), sg.SimpleButton('EXIT', button_color=(sg.YELLOWS[0], sg.GREENS[0]))]]
|
||||||
|
# notice this is NOT the usual LayoutAndRead call because you don't yet want to read the form
|
||||||
|
# if you call LayoutAndRead from here, then you will miss the first button click
|
||||||
|
form.Layout(layout)
|
||||||
|
# ---===--- Loop taking in user input and using it to query HowDoI web oracle --- #
|
||||||
|
while True:
|
||||||
|
button, value = form.Read()
|
||||||
|
if button == 'SEND':
|
||||||
|
print(value)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Shows a form that's a running counter
|
||||||
|
# this is the basic design pattern if you can keep your reading of the
|
||||||
|
# form within the 'with' block. If your read occurs far away in your code from the form creation
|
||||||
|
# then you will want to use the NonBlockingPeriodicUpdateForm example
|
||||||
|
def NonBlockingPeriodicUpdateForm_ContextManager():
|
||||||
|
with sg.FlexForm('Running Timer', auto_size_text=True) as form:
|
||||||
|
text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), text_color='red', justification='center')
|
||||||
|
layout = [[sg.Text('Non blocking GUI with updates', justification='center')],
|
||||||
|
[text_element],
|
||||||
|
[sg.T(' ' * 15), sg.Quit()]]
|
||||||
|
form.LayoutAndRead(layout, non_blocking=True)
|
||||||
|
|
||||||
|
for i in range(1,500):
|
||||||
|
text_element.Update('{:02d}:{:02d}.{:02d}'.format((i // 100) // 60, (i // 100) % 60, i % 100))
|
||||||
|
button, values = form.ReadNonBlocking()
|
||||||
|
if values is None or button == 'Quit': # if user closed the window using X
|
||||||
|
break
|
||||||
|
time.sleep(.01)
|
||||||
|
else:
|
||||||
|
# if the loop finished then need to close the form for the user
|
||||||
|
form.CloseNonBlockingForm()
|
||||||
|
|
||||||
|
# Use this context-manager-free version if your read of the form occurs far away in your code
|
||||||
|
# from the form creation (call to LayoutAndRead)
|
||||||
|
def NonBlockingPeriodicUpdateForm():
|
||||||
|
# Show a form that's a running counter
|
||||||
|
form = sg.FlexForm('Running Timer', auto_size_text=True)
|
||||||
|
text_element = sg.Text('', size=(10, 2), font=('Helvetica', 20), justification='center')
|
||||||
|
form_rows = [[sg.Text('Non blocking GUI with updates')],
|
||||||
|
[text_element],
|
||||||
|
[sg.T(' ' * 15), sg.Quit()]]
|
||||||
|
form.LayoutAndRead(form_rows, non_blocking=True)
|
||||||
|
|
||||||
|
for i in range(1,50000):
|
||||||
|
text_element.Update('{:02d}:{:02d}.{:02d}'.format((i//100)//60, (i//100)%60, i%100))
|
||||||
|
button, values = form.ReadNonBlocking()
|
||||||
|
if values is None or button == 'Quit': # if user closed the window using X or clicked Quit button
|
||||||
|
break
|
||||||
|
time.sleep(.01)
|
||||||
|
else:
|
||||||
|
# if the loop finished then need to close the form for the user
|
||||||
|
form.CloseNonBlockingForm()
|
||||||
|
del(form)
|
||||||
|
|
||||||
|
def DebugTest():
|
||||||
|
# SG.Print('How about we print a bunch of random numbers?', , size=(90,40))
|
||||||
|
for i in range (1,300):
|
||||||
|
sg.Print(i, randint(1, 1000), end='', sep='-')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# SG.SetOptions(border_width=1, font=("Helvetica", 10), button_color=('white', SG.BLUES[0]), slider_border_width=1)
|
||||||
|
NonBlockingPeriodicUpdateForm_ContextManager()
|
||||||
|
NonBlockingPeriodicUpdateForm()
|
||||||
|
Everything_NoContextManager()
|
||||||
|
Everything()
|
||||||
|
ChatBot()
|
||||||
|
ProgressMeter()
|
||||||
|
SourceDestFolders()
|
||||||
|
ChatBot()
|
||||||
|
DebugTest()
|
||||||
|
sg.MsgBox('Done with all recipes')
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
exit(69)
|
|
@ -0,0 +1,89 @@
|
||||||
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
|
MAX_NUMBER_OF_THREADS = 12
|
||||||
|
|
||||||
|
def eBaySuperSearcherGUI():
|
||||||
|
# Drop Down list of options
|
||||||
|
configs = ('0 - Gruen - Started 2 days ago in Watches',
|
||||||
|
'1 - Gruen - Currently Active in Watches',
|
||||||
|
'2 - Alpina - Currently Active in Jewelry',
|
||||||
|
'3 - Gruen - Ends in 1 day in Watches',
|
||||||
|
'4 - Gruen - Completed in Watches',
|
||||||
|
'5 - Gruen - Advertising',
|
||||||
|
'6 - Gruen - Currently Active in Jewelry',
|
||||||
|
'7 - Gruen - Price Test',
|
||||||
|
'8 - Gruen - No brand name specified')
|
||||||
|
|
||||||
|
us_categories = ('Use Default with no change',
|
||||||
|
'All - 1',
|
||||||
|
'Jewelry - 281',
|
||||||
|
' Watches - 14324',
|
||||||
|
' Wristwatches - 31387',
|
||||||
|
' Pocket Watches - 3937',
|
||||||
|
'Advertising - 34',
|
||||||
|
' Watch Ads - 165254'
|
||||||
|
)
|
||||||
|
|
||||||
|
german_categories =('Use Default with no change',
|
||||||
|
'All - 1',
|
||||||
|
'Jewelry - 281',
|
||||||
|
' Watches - 14324',
|
||||||
|
' Wristwatches - 31387',
|
||||||
|
' Pocket Watches - 3937',
|
||||||
|
'Advertising - 1',
|
||||||
|
' Watch Ads - 19823'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# the form layout
|
||||||
|
with sg.FlexForm('EBay Super Searcher', auto_size_text=True) as form:
|
||||||
|
with sg.FlexForm('EBay Super Searcher') as form2:
|
||||||
|
layout_tab_1 = [[sg.Text('eBay Super Searcher!', size=(60,1), font=('helvetica', 15))],
|
||||||
|
[sg.Text('Choose base configuration to run')],
|
||||||
|
[sg.InputCombo(configs)],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.InputText(),sg.Text('Choose Destination Folder'), sg.FolderBrowse(target=(sg.ThisRow,0))],
|
||||||
|
[sg.InputText(),sg.Text('Custom text to add to folder name')],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.Checkbox('US', default=True, size=(15, 1)), sg.Checkbox('German', size=(15, 1), default=True, )],
|
||||||
|
[sg.Radio('Active Listings','ActiveComplete', default = True,size=(15, 1)), sg.Radio('Completed Listings', 'ActiveComplete', size=(15, 1))],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.Checkbox('Save Images', size=(15,1)),sg.Checkbox('Save PDFs', size=(15,1)), sg.Checkbox('Extract PDFs', size=(15,1))],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.Text('Time Filters')],
|
||||||
|
[sg.Radio('No change','time', default=True),sg.Radio('ALL listings','time'),sg.Radio('Started 1 day ago','time', size=(15,1)),sg.Radio('Started 2 days ago','time', size=(15,1)), sg.Radio('Ends in 1 day','time', size=(15,1))],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.Text('Price Range'), sg.InputText(size=(10,1)),sg.Text('To'), sg.InputText(size=(10,1))],
|
||||||
|
[sg.Text('_'*100, size=(80,1))],
|
||||||
|
[sg.Submit(button_color=('red', 'yellow')), sg.Cancel(button_color=('white', 'blue')), sg.Text(f'{MAX_NUMBER_OF_THREADS} Threads will be started')]]
|
||||||
|
|
||||||
|
|
||||||
|
# First category is default (need to special case this)
|
||||||
|
layout_tab_2 = [[sg.Text('Choose Category')],
|
||||||
|
[sg.Text('US Categories'),sg.Text('German Categories')],
|
||||||
|
[sg.Radio(us_categories[0],'CATUS', default=True), sg.Radio(german_categories[0], 'CATDE', default=True)]]
|
||||||
|
|
||||||
|
for i,cat in enumerate(us_categories):
|
||||||
|
if i == 0: continue # skip first one
|
||||||
|
layout_tab_2.append([sg.Radio(cat,'CATUS'), sg.Radio(german_categories[i],'CATDE')])
|
||||||
|
|
||||||
|
|
||||||
|
layout_tab_2.append([sg.Text('_' * 100, size=(75, 1))])
|
||||||
|
layout_tab_2.append([sg.Text('US Search String Override')])
|
||||||
|
layout_tab_2.append([sg.InputText(size=(100,1))])
|
||||||
|
layout_tab_2.append([sg.Text('German Search String Override')])
|
||||||
|
layout_tab_2.append([sg.InputText(size=(100,1))])
|
||||||
|
layout_tab_2.append([sg.Text('Typical US Search String')])
|
||||||
|
layout_tab_2.append([sg.InputText(size=(100,1), default_text='gruen -sara -quarz -quartz -embassy -bob -robert -elephants -adidas -LED ')])
|
||||||
|
layout_tab_2.append([sg.Text('_' * 100, size=(75, 1))])
|
||||||
|
layout_tab_2.append([sg.Submit(button_color=('red', 'yellow'),auto_size_text=True), sg.Cancel(button_color=('white', 'blue'), auto_size_text=True)])
|
||||||
|
|
||||||
|
results = sg.ShowTabbedForm('eBay Super Searcher', (form,layout_tab_1,'Where To Save'), (form2, layout_tab_2, 'Categories & Search String'))
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
results = eBaySuperSearcherGUI()
|
||||||
|
print(results)
|
||||||
|
sg.MsgBox('Results', results)
|
Loading…
Reference in New Issue