How to make my for loop work in openpyxl? - python

Hi I am in process of writing a python code to input search and edit data into excel.
my chosen format for the UI is PySimpleGUI and my xlsx package is openpyxl.
I have been coding for approximately 3 weeks.
I probably bit off more than I can chew but I won't give up.
I need to know why my for loop won't work so I can fix it.
here is the offending code:
import PySimpleGUI as sg
import pandas as pd
import openpyxl as op
def weekly_wage():
filename = 'Payroll.xlsx'
wb = op.load_workbook(filename, data_only=True)
ws = wb.worksheets[0]
ws_tables = []
layout = [
[sg.Text('Employee Name', size=(15,1), font=('ariel', 16)), sg.InputText(key='-Name-', font=('ariel',16))],
[sg.Text('Week Ending', size=(15,1), font=('ariel',16)), sg.InputText(key='-Date-', font=('ariel',16))],
[sg.Submit(font=('ariel', 16))],
[sg.Text('The Weekly Wage is:', font=('ariel', 16))],
[sg.Output(size=(10, 1))]
]
window = sg.Window('Gross Wages Search', layout, size=(450,250))
# Event Loop
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
if event == 'Submit':
if event == 'Submit':
key1 = window['-Name-'] # this gives a callable object to the Input keys
key2 = window['-Date-']
rows = ws.iter_rows()
name = []
date = []
gross = []
for a,b,aw in rows:
name.append(a)
date.append(b)
gross.append(aw)
if a.value == key1 and b.value == key2:
print (aw.value)
break
else:
print('Try Again')
break
window.close()
weekly_wage()
I have poored over the openpyxl docs for hours per night, and I have performed that many google searches I now have shares!!

Related

How to make progress bar work using PySimpleGUI in Python?

I am using PySimpleGui's one-line progress meter to show the files processed while my main function is running. It is working for the most part, however when I change the parameter from value of 1000 to my variable total_files it just flickers and never stays on screen. It's only when I keep the parameter to a value of 1000 does the screen stay put...but I want to just show the progress of the true amount of files processed.
For example, I have 8 files I am processing as a test. I want the progress bar to stay on the screen and just show those 8 files processed. Does anyone know what I'm doing wrong?
Documentation on progress meter:
https://www.pysimplegui.org/en/latest/#progress-meters
for i in range(1,total_files):
sg.one_line_progress_meter('Loading...', i+1, 1000, 'key','Files Processed', orientation="horizontal")
Below is my code:
import PySimpleGUI as sg, pandas as pd, os, time
from pathlib import (
Path,
)
def convert_to_csv(input_folder, output_folder, dataframe2, dataframe3):
start = time.time()
# Parentsupplierbrand mapping file. Change the filepath to where the file is stored on your computer.
df2 = pd.read_csv(rf"{dataframe2}", sep="|")
# KCS Section Codes mapping file. Change the filepath to where the file is stored on your computer.
df3 = pd.read_excel(rf"{dataframe3}")
# loop through only text files in the input folder and convert data
for files in os.listdir(input_folder):
if files.endswith(".txt"):
df1 = pd.read_csv(rf"{input_folder}/{files}", sep="\t", encoding="latin-1")
df1["BrandName"] = df1["Comp_Brand"].apply(
xlookup, args=(df2["BrandID"], df2["BrandName"])
)
# map columns to the desired headers
df1.rename(
columns={
"BrandName": "Competitor_Name",
"Comp_PN": "Competitor_PartNumber",
"CQ_Brand": "AAIA_Code",
"CQ_PN": "PartNumber",
},
inplace=True,
)
remove_columns = df1.loc[
:, ~df1.columns.isin(["Comp_Brand", "CQ_Desc", "PartID"])
]
new_columns = [
"Competitor_Name",
"Competitor_PartNumber",
"AAIA_Code",
"PartNumber",
]
table_output = remove_columns[new_columns]
# below code for constructing file name convention
kcs_section_codes = df3.values.tolist()
stringtwo = table_output.loc[0][2] # stringtwo is the AAIA_Code
for code in kcs_section_codes:
if code[0] == stringtwo:
# stringone is the matching value under BrandName in the KCS_Section_Codes file
# by looking up AAIA_Code from the initial file in the BrandCode column from the KCS_Section_Codes file
stringone = "".join(
e for e in code[1] if e.isalnum()
) # replace any character that is not a letter or number with an empty character.
filename = rf"{stringone}-{stringtwo}_Interchanges.txt"
outputfile = rf"{output_folder}/{filename}"
table_output.to_csv(
outputfile,
sep="\t",
index=False,
columns=[
"Competitor_Name",
"Competitor_PartNumber",
"AAIA_Code",
"PartNumber",
],
)
# progress bar
total_files = sum([len(files) for r, d, files in os.walk(output_folder)])
for i in range(1, total_files):
sg.one_line_progress_meter(
"Loading...",
i + 1,
1000,
"key",
"Files Processed",
orientation="horizontal",
)
# code below for returning stats of main function in a popup window
total_files = sum([len(files) for r, d, files in os.walk(output_folder)])
end = time.time()
result_time = end - start
time_formatted = time.strftime("%H:%M:%S", time.gmtime(result_time))
sg.popup_no_titlebar(
f"Done! :)\nTotal Files Processed: {total_files}\nExecution Time (hh:mm:ss): {time_formatted}"
)
def xlookup(lookup_value, lookup_array, return_array, if_not_found: str = ""):
match_value = return_array.loc[lookup_array == lookup_value]
if match_value.empty:
return f'"{lookup_value}" not found!' if if_not_found == "" else if_not_found
else:
return match_value.tolist()[0]
def is_valid_path(filepath):
if filepath and Path(filepath).exists():
return True
sg.popup_error("Filepath not correct")
return False
def gui():
sg.theme("DarkBlue3") # Add a touch of color
# All the stuff inside your window.
layout = [
[
sg.Text("Input Folder: "),
sg.Input(key="-IN-"),
sg.FolderBrowse(),
],
[sg.Text("Output Folder: "), sg.Input(key="-OUT-"), sg.FolderBrowse()],
[
sg.Text("Data Mapping File: Parent Supplier Brand"),
sg.Input(key="-DF2-"),
sg.FileBrowse(),
],
[
sg.Text("Data Mapping File: KCS Section Codes"),
sg.Input(key="-DF3-"),
sg.FileBrowse(),
],
[sg.Exit(), sg.Button("Convert to CSV")],
]
# Create the Window
window = sg.Window("Data Conversion", layout)
# Event Loop to process "events" and get the "values" of the inputs
while True:
event, values = window.read()
if (
event == sg.WIN_CLOSED or event == "Cancel"
): # if user closes window or clicks cancel
break
if event in (sg.WIN_CLOSED, "Exit"):
break
if (
event == "Convert to CSV"
): # if user clicks covert to csv button then call convert_to_csv function using inputs
if (is_valid_path(values["-IN-"])) and (is_valid_path(values["-OUT-"])):
convert_to_csv(
input_folder=values["-IN-"],
output_folder=values["-OUT-"],
dataframe2=values["-DF2-"],
dataframe3=values["-DF3-"],
)
window.close()
if __name__ == "__main__":
gui()
Following code update the progressbar without any time delay, so it will show in short time and reach the maximum value immediately, then the window for progressbar closed.
for i in range(1, total_files):
sg.one_line_progress_meter(
"Loading...",
i + 1,
1000,
"key",
"Files Processed",
orientation="horizontal",
)
Reduce your code to ask question, it will help people to help.
Demo code:
from time import sleep
import PySimpleGUI as sg
from threading import Thread
def job(window):
window.write_event_value("Update", 0)
for i in range(1, 11):
sleep(0.2) # Simulate each step in the job
window.write_event_value("Update", i) # Cannot update GUI not in the main thread
window.write_event_value("Done", None)
sg.theme("DarkBlue3")
layout = [[sg.Button("Submit"), sg.Button("Exit")],]
window = sg.Window("Title", layout)
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, "Exit"):
break
elif event == "Submit":
thread = Thread(target=job, args=(window, ), daemon=True)
thread.start()
elif event == "Update":
i = values[event]
sg.one_line_progress_meter("Loading...", i, 10, "key", "Files Processed", orientation="horizontal")
elif event == "Done":
print('Job done !')
window.close()

How can I pass multiple user input values from PySimpleGUI to a function, then output result to PySimpleGUI?

GitHub for this project
I am building a financial trading profit/loss calculator. I have built the script to function in the command line interface (main_cli.py).
Now, I am trying to convert to a desktop GUI via PySimpleGUI, but am having difficulty with passing multiple user input values from PySimpleGUI (main_gui.py) to the calculator function (functions.py), to then output the result in the same PySimpleGUI window.
When I run the main_gui.py script, it still asks for user_input in the CLI.
Just pass values to your function, then convert all fields into your data for the function to calculate the result, and return it back to event loop to update the GUI.
Note: No any entry validation in the code.
import PySimpleGUI as sg
def calculate(values):
ticker = ticker_list[[values[("Ticker", i)] for i in range(len(tickers))].index(1)]
tick_size, contract_amount = tickers[ticker]
position = "Long" if values[("Position", 0)] else "Short"
purchase_price = float(values['Entry'])
selling_price = float(values['Exit'])
contract_size = float(values['Contract'])
tick_value = (((purchase_price - selling_price)*contract_size)/tick_size)
dollar_value = (tick_value*contract_amount)
if position == "Long":
tick_value = abs(tick_value)
dollar_value = abs(dollar_value)
return tick_value, dollar_value
tickers = {
'ES' : (0.25, 12.50),
'NQ' : (0.25, 5.00),
'RTY': (0.10, 5.00),
'YM' : (1.00, 5.00),
'GC' : (0.10, 10.00),
'CL' : (0.01, 10.00),
}
ticker_list = list(tickers.keys())
positions = ['Long', 'Short']
keys = {
'Entry':'Entry price',
'Exit':'Exit price',
'Contract':'Contract size',
}
sg.theme('Dark')
sg.set_options(font=('Helvetica', 10))
layout = [
[sg.Text("Ticker:", size=10)] + [sg.Radio(tick, "Radio 1", key=("Ticker", i)) for i, tick in enumerate(tickers)],
[sg.Text("Position:", size=10)] + [sg.Radio(pos, "Radio 2", key=("Position", i)) for i, pos in enumerate(positions)]] + [
[sg.Text(text, size=10), sg.InputText(size=10, expand_x=True, key=key)] for key, text in keys.items()] + [
[sg.Text("Result"), sg.Text(text_color="white", key="Output")],
[sg.Push(), sg.Button("Reset"), sg.Button("Calculate", button_color=('white', '#007339'))],
]
window = sg.Window('Futures Profit/Loss Calculator', layout=layout)
while True:
event, values = window.read()
print(event, values)
if event == sg.WIN_CLOSED:
break
elif event == "Calculate":
tick_value, dollar_value = calculate(values)
result = f"ticks: {tick_value} | profit/loss: ${dollar_value}"
window['Output'].update(result)
elif event == "Reset":
for key in keys:
window[key].update('')
window.close()
If this function take long time to finish the caluclation, you can call method window.perform_long_operation to run your function, Lambda expression can help to pass the arguments here, then update your GUI when the event '-FUNCTION COMPLETED-' generated, like
elif event == "Calculate":
window.perform_long_operation(lambda x=values:calculate(x), '-FUNCTION COMPLETED-')
elif event == '-FUNCTION COMPLETED-':
tick_value, dollar_value = values[event]
result = f"ticks: {tick_value} | profit/loss: ${dollar_value}"
window['Output'].update(result)

PysimpleGui window gets rescaled to smaller size when i recall the function in which pysimpleGUI is present

I am creating an app in which i want display data in tabular format i created a GUI windows that displays the data properly but whenever i recall the same function or any other GUI function my GUI window Gets Resized/Rescaled to a smaller size
def tableGUIP(file):
data = []
header_list = []
df = pd.read_csv(file, sep=',', engine='python', header=None)
data = df.values.tolist() # read everything else into a list of rows
header_list = df.iloc[0].tolist()
data = df[1:].values.tolist()
layout = [
[sg.Table(values=data,
headings=header_list,
display_row_numbers=False,
auto_size_columns=True,
row_height=20,
num_rows=min(20, len(data)))],
[sg.Text('Column', size =(15, 1))],
[sg.Button("Rank"),sg.Button("Live"),sg.Button("2020"),sg.Button("Area"),sg.Button("Density"),sg.Button("Growth"),sg.Button("World %"),sg.Exit()]
]
window = sg.Window('Table', layout, grab_anywhere=False,use_default_focus=True,keep_on_top=True,finalize=True)
event, values = window.read()
while True:
if event in (None, 'Exit'):
sortcol('Exit')
break
# in ('Rank','2022 (Live)', '2020 Population', 'Area', 'Density', 'Growth Rate', 'World %'):
if event == "Rank":
sortcol('Rank')
break
elif event=="Live":
sortcol('2022 (Live)')
break
elif event=="2020":
sortcol('2020 Population')
break
elif event=="Area":
sortcol('Area')
break
elif event=="Density":
sortcol('Density')
break
elif event=="Growth":
sortcol('Growth Rate')
break
elif event=="World %":
sortcol('World %')
break
window.close()
This is the GUI windows after the first occurrence occurs
This is the GUI windows When the first occurrence occurs

how to pysimplegui manage dataframe in excel?

i want to delete dataframe unnamed in red arrow , and the i want to add some text when i click submit i have index from 1 not in 0 like this
Data_entry.xlsx
can you give me solution about this problem ?
in above my code in python 3
import PySimpleGUI as sg
import pandas as pd
# importing openpyxl module
import openpyxl
# Give the location of the file
path = "C:\\Users\\Admin\\Desktop\\WEB\\PYTHON_PYSIMPLEGUI\\Data_Entry.xlsx"
sg.theme('DarkGreen7')
EXCEL_FILE = 'Data_Entry.xlsx'
df = pd.read_excel(EXCEL_FILE)
my_img = sg.Image(r'C:\Users\master\Desktop\WEB\PYTHON_PYSIMPLEGUI\logo.png')
"""
# Template Taskbar
menu_def = [['File', ['Open', 'Save', 'Exit',]],
['Edit', ['Paste', ['Special', 'Normal',], 'Undo'],],
['Help', 'About...'],]
"""
menu_def = [['File', ['Exit']],['Help', 'About...'],]
layout = [
[sg.Menu(menu_def)],
[sg.Column([[my_img]], justification='center')],
[sg.Text('Simacan ( SIstem Monitoring And Controling Absen Nilai')],
[sg.Text('Nama', size=(15,1)), sg.InputText(key='Nama')],
[sg.Text('Kehadiran', size=(15,1)), sg.Combo(['Hadir', 'Sakit', 'Tidak Masuk'], key='Keterangan')],
[sg.Submit(), sg.Exit()]
]
window =sg.Window('Aplikasi Simacan versi 1.2', layout,size=(800, 600), font='Courier 12')
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event == 'Submit':
df = df.append(values, ignore_index=True)
df.to_excel(EXCEL_FILE, index=True)
sg.popup('Data saved !')
print(event, values)
window.close()
There's are some issues here.
Create a blank excel file to store your data.
Open excel file as a dataframe, option index_col set the index column or you may get Unnamed: 0 column as index in your dataframe.
df = pd.read_excel(EXCEL_FILE, index_col=[0])
There's one extra item 0 in dictionary values, it is the key of sg.Menu, should remove it before you append the values to your dataframe by
del values[0]
You can re-index your dataframe by
df.index = np.arange(1, len(df)+1)
You can save dataframe to your excel file only before end of you script, of course, write each new record to your excel file is still fine.

Popups in PySimpleGUI for displaying a list of objects and then place selected object in an inputbox

I am trying to create a popup that is triggered when the SELECTION button is pressed. It should show a column from a particular excel sheet, users can select entries and this will populate the STRATEGY input box.
I have this code:
import PySimpleGUI as sg
import pandas as pd
columns = ["STRATEGY", "FOR_SHOW"]
param = (20,3) # size of the main window
df_strategies = pd.read_excel(r'path/Strategies.xlsx', 'Strategies')
def GUI(df_strategies):
sg.theme('Dark Brown 1')
listing = [sg.Text(u, size = param) for u in columns]
core = [
sg.Input(size = param, key='STRATEGY'),
sg.Listbox(list(df_strategies['STRATEGIES ']), size=(20,2), enable_events=False)
]
mesh = [[x,y] for (x,y) in list(zip(listing, core))]
layout =[[sg.Button("SEND")]]+ mesh
mesh[0].append(sg.Button('SELECTION'))
window = sg.Window('GUI', layout, font='Courier 12').Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "SEND":
break
elif event == 'SELECTION':
sg.popup_scrolled(list(df_strategies['STRATEGIES ']))
else:
print("OVER")
window.close()
GUI(df_strategies)
Unfortunately this sg.popup_scrolled only shows the text itself with no option to actually select.
Essentially, i am trying to create something that is very similar to the FOR_SHOW listbox apart from it is shown in a Popup and the selected entry populates the STRATEGY input box.
You will have to create own popup from scratch - instead of using popup_scrolled - and then you can use Listbox and/or other widget(s).
import PySimpleGUI as sg
import pandas as pd
# --- functions ---
def GUI_POPUP(text, data):
layout = [
[sg.Text(text)],
[sg.Listbox(data, size=(20,5), key='SELECTED')],
[sg.Button('OK')],
]
window = sg.Window('POPUP', layout).Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'OK':
break
else:
print('OVER')
window.close()
print('[GUI_POPUP] event:', event)
print('[GUI_POPUP] values:', values)
if values and values['SELECTED']:
return values['SELECTED']
def GUI_MAIN(data):
strategies = data['STRATEGIES'].tolist()
for_show = data['FOR_SHOW'].tolist()
layout = [
[sg.Button('SEND')],
[sg.Text('STRATEGY', size=(20,1)), sg.Input(size=(20,1), key='STRATEGY'), sg.Button('SELECTION')],
[sg.Text('FOR_SHOW', size=(20,1)), sg.Listbox(for_show, size=(20,5), key='FOR_SHOW', enable_events=False)],
]
window = sg.Window('GUI', layout).Finalize()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'SEND':
break
elif event == 'SELECTION':
selected = GUI_POPUP('STRATEGY', strategies)
print('selected:', selected)
if selected:
window['STRATEGY'].update(selected[0])
mask = (data['STRATEGIES'] == selected[0])
for_show = data['FOR_SHOW'][ mask ].tolist()
window['FOR_SHOW'].update(for_show)
else:
print('OVER')
window.close()
print('[GUI_MAIN] event:', event)
print('[GUI_MAIN] values:', values)
# --- main ---
if __name__ == '__main__':
#df_strategies = pd.read_excel(r'path/Strategies.xlsx', 'Strategies')
df_strategies = pd.DataFrame({
'STRATEGIES': ['A', 'B', 'C'],
'FOR_SHOW': [1, 2, 3]
})
GUI_MAIN(df_strategies)
BTW:
Using print( sg.__file__ ) you can get path to source code
and you can check how popup_scrolled was created and use it to create own popups.
On my Linux Mint I have source code in
/usr/local/lib/python3.8/dist-packages/PySimpleGUI

Categories