0

TL;DR: I'm basically trying to work out how to merge these two example codes from the PySimpleGui Github: https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Layout_Extend.py and https://github.com/PySimpleGUI/PySimpleGUI/blob/master/DemoPrograms/Demo_Multiline_Right_Click_Menu_Clipboard.py where the element key passed to the clipboard function has been dynamically created...

Using the docs for PySimpleGui and the plethora of info on both stackoverflow and the Github demos I have a 'simple' gui made that can add and remove Input fields and I have everything sorted for that side of things (ie adding elements to the GUI using layout_extend, removing elements using .hide_row() and keeping a list of removed values etc...

Like the layout_extend demo I am using a Frame, and adding GUI elements to it using an iterator to ensure unique 'keys' are created.

##CODE SNIPPET AS MOST OF IT IS PRETTY MUCH THE SAME AS THE EXAMPLE DEMO CODE WITH MINOR CHANGES

right_click_menu = ['', ['Copy', 'Paste', 'Select All', 'Cut']]

def do_clipboard_operation(event, window, element):
    #Same as Demo Code Here No Point Copying!

...
...

if event in right_click_menu[1]:
    do_clipboard_operation(event, program_window, element)
if event == '-ADDPATH-':
    program_window.extend_layout(program_window['-FRAME1-'], [[sg.Text('PATH:'),sg.Input(key=f'-PATH{i}-', rick_click_menu=right_click_menu), sg.FolderBrowse(initial_folder="/base/path", key=f'-BROWSEPATH{i}-'), sg.Button('Remove Path', key=f'-REMOVEPATH{i}-')]])
    i += 1
...
...

When a User is presented with an input field that they want to put a long string of text in (in my case folder paths) there is a desire to the 'right-click context menu' so I wanted to look at adding it.

My issue comes that the Multi-line Rick Click Menu Clipboard demo needs the triggering element key passed to the function (which is then used to add / remove text into), but because I have 'dynamically generated' Key names for the elements I need to work out which element key activated the right click menu before being able to pass it and I just don't know how to do that.....

The code I've written is basically identical to the Layout_extend demo.....

I appreciate that PySimpleGui, as awesome as it is, might not really be the best tool to handle dynamic GUIs but as I've always struggled with OO programming its pretty much the only way I've been able to build a functional Python GUI to date....

If this isn't possible at this stage, that's ok, but least I asked (which makes me feel better when I inevitably get someone saying 'its really annoying I can't right click' when they use it)!!

Thanks in advance as always!!

Owen.

EDIT:: Added Code (Had to remove some references to specific details - root paths, and other such info, placed '<' '>' around placeholders for where data existed - but the code is the same:


#!/bin/python3

import PySimpleGUI as sg
import os.path
from getpass import getuser
from string import ascii_letters, digits
from random import choice as rchoice

SPLASH_IMAGE_PATH=r'<AN IMAGE FILE LOCATION>'
OUT_PATH='./'
DISPLAY_SPLASH_MS = 1500
REMOVE_LIST=[]
VERSION_NUM='0.3.0'

splash_theme = {'BACKGROUND': '#ffcc00',
                'TEXT': '#000000',
                'INPUT': '#ffffff',
                'TEXT_INPUT': '#ffffff',
                'SCROLL': '#ffffff',
                'BUTTON': ('white', '#000000'),
                'PROGRESS': ('#ffffff', '#ffffff'),
                'BORDER': 0,
                'SLIDER_DEPTH': 0,
                'PROGRESS_DEPTH': 0
}

right_click_menu = ['', ['Copy','Paste','Select All','Cut']]

def do_clipboard_operation(event, window, element):
    print(event)
    print(window)
    print(element)

##Random string generator: https://www.stackoverflow.com/questions/2257441/random-sting-generation-with-upper-case-letters-and-digits (Ignacio Vazquez-Abrams answer)
def random_string_gen(size=8, chars=ascii_letters + digits):
    return ''.join(rchoice(chars) for _ in range(size))

def main():
    sg.theme_add_new('SplashScreen', splash_theme)
    sg.theme('SplashScreen')
    splash_window= sg.Window('Uploader', [[sg.Image(SPLASH_IMAGE_PATH)], [sg.Text('Uploader', font=("any",20))], [sg.Text(f'Version: {VERSION_NUM}', font=('any',12))]], element_justification='c', no_titlebar=True, keep_on_top=True).read(timeout=DISPLAY_SPLASH_MS, close=True)

    sg.theme('DarkAmber')
    layout = [ [sg.Text('Please Enter FULL Paths. Use The Browse Button To Navigate, Or Copy Path Into Field.')],
             [sg.Text('Use The "Add Path" Button To Add More Rows, And The "Remove Path" Button To Remove A Row.')],
             [sg.Text('When Finished, Press The "Upload" Button - This Will Take Time To Process.')],
             [sg.Frame('Paths:', [[sg.Text('PATH:'), sg.Input(key='-PATH0-', right_click_menu=right_click_menu), sg.FolderBrowse(initial_folder="<ROOT PATH>",  key='-BROWSEPATH0-' ), sg.Button('Remove Path', key='-REMOVEPATH0-')]], key='-FRAME1-')],
             [sg.OK('Upload',key='-RUN-'), sg.Button('Add Additional Path', key='-ADDPATH-'), sg.Cancel('Exit', key='-EXIT-')]
    ]

    program_window = sg.Window('Uploader', layout)
    i = 1
    num_rows=1
    user=str(getuser())
    while True:
        event, values = program_window.read()
        if event == sg.WIN_CLOSED or event == '-EXIT-':
            break
        if event in right_click_menu[1]:
            print(values)
           # do_clipboard_operation(event, program_window, element)
        if event == '-ADDPATH-':
            ##Add new row to output window. uses f' ' string formatting to use the i iterator to create unique keys
            program_window.extend_layout(program_window['-FRAME1-'], [[sg.Text('PATH:'), sg.Input(key=f'-PATH{i}-', right_click_menu=right_click_menu), sg.FolderBrowse(initial_folder="<ROOT PATH>", key=f'-BROWSEPATH{i}-' ), sg.Button('Remove Path', key=f'-REMOVEPATH{i}-')]])
            i += 1
            num_rows += 1
        if event == '-RUN-':
            ##Iterate over values and remove duplicates (both the text field and the browse button can be populated with the same data so duplicates cannot be avoided
            ##Remove blank values (can occur when path is pasted into text field and browse button not used, and remove any paths that occur in the remove_list. generated when lines are removed
            ##Remove items that appear in REMOVE_LIST (created when removing lines)
            ##Output to a new out_list as items in value cannot be changed
            ##Validate User input to: ensure no <AVOID PATH> paths, no invalid paths, at least one path is submitted, and path submitted actually contains an <SPECIFIC FILES>... Show popup errors if any found and allow user to remove / alter offending paths.
            ##If all is OK generate a random string with the username appended and write all valid paths into a temp file with that random name. This is for back-end program to run.
            out_list = []
            error_list = []
            avoid_path_error=False
            invalid_path=False
            for key, value in values.items():
                if value not in out_list and not value == '' and not value in REMOVE_LIST:
                    if '<AVOID PATH>' in value:
                        avoid_path_error=True
                        sg.popup('One Or More Of Your Paths Is Located In The <AVOID PATH> And Cannot Be Used As An Input!\n\nPlease Remove The Offending Path.', title='<AVOID PATH> FOUND', keep_on_top=True, custom_text=('Sorry'), button_color=('white', 'red'))
                        break
                    elif not os.path.isdir(value):
                        invalid_path=True
                        sg.popup(f'The Following Invalid Path Has Been Entered And Does Not Seem To Exist / Is Unreadable:\n\n{value}', title='INVALID PATH ENTERED', keep_on_top=True, custom_text=('Oops!'), button_color=('white', 'red'))
                        break
                else:
                    out_list.append(value)
                    if not avoid_path_error ==  True and not invalid_path == True:
                        if not out_list:
                            sg.popup('You Have Not Added Any Paths!!! Please Try Again', title='NO PATH ENTERED', keep_on_top=True, custom_text=('Oops!'), button_color=('white', 'red'))
                    else:
                        for path in out_list:
                            for files in os.listdir(path):
                                if '<SPECIFIC FILE>' in files.lower():
                                    break
                                else:
                                    error_list.append(path)
                        if error_list:
                            error_list_string = '\n'.join(error_list)
                            sg.popup(f'The Following Path(s) Do NOT Contain <SPECIFIC FILE>...\nPlease ONLY Add Full Paths:\n\n{error_list_string}', title='NO <SPECIFIC PATH> FOUND', keep_on_top=True, custom_text=('Sorry!'), button_color=('white', 'red'))
                        else:
                            print(random_string_gen() + '.' + user)
                            break
            if '-REMOVEPATH' in event:
                ##Remove Line from window. values Dict cannot be altered, so need to generate a new list with the values of the data held in the value dict: -PATHx- and -BROWSEPATHx-
                ##Where x is the unique iterator number used to ensure unique keys. The Event also uses the same unique iterator key: -REMOVEPATHx- so replacing -REMOVEPATH with -PATH and -BROWSEPATH
                ##generates the keys we need. call the hide_row() function to remove row from window, and append both -PATHx- and -BROWSEPATHx-.
                folder_path=str(event).replace("-REMOVEPATH", "-PATH")
                browse_path=str(event).replace("-REMOVEPATH", "-BROWSEPATH")
                num_rows -= 1
                if num_rows == 0:
                    program_window[event].hide_row()
                    program_window.extend_layout(program_window['-FRAME1-'], [[sg.Text('PATH:'), sg.Input(key=f'-PATH{i}-', right_click_menu=right_click_menu), sg.FolderBrowse(initial_folder="<ROOT PATH>", key=f'-BROWSEPATH{i}-' ), sg.Button('Remove Path', key=f'-REMOVEPATH{i}-')]])
                    i += 1
                    num_rows = 1
                else:
                    program_window[event].hide_row()
                    REMOVE_LIST.append(values[folder_path])
                    REMOVE_LIST.append(values[browse_path])
    program_window.close()


if __name__ == "__main__":
    main()
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Owen Morgan
  • 301
  • 3
  • 9
  • 1
    Can you add your actual code to see how far you've gotten? I'd like to see how you integrated the snippet you included in the PySimpleGui window. I'll take a look and see if I can help. – Robin Sage Jul 19 '21 at 07:39
  • @RobinSage Added to Q :) I had to replace some references to potentially 'sensitive' data by adding a instead, just means if anyone wants to run it from the code they would need to just add a couple of bits of generic data for themselves (root paths, image paths etc.) – Owen Morgan Jul 19 '21 at 17:39
  • 1
    Alright, I'll take a look at and get back to you. – Robin Sage Jul 19 '21 at 18:58

0 Answers0