PySimpleGUI - Updating a listbox on a button event - python-3.x

I'm trying to make a Python program with a UI that shows simple widget like a frame, a listbox and a table. I would like to populate the listbox with input from the user. So I create the listbox and "fill it" qith a blank list, then the user type in a name, click the button and the list will have that name in it and the name will be shown in the listbox. I quite manage to do this, but the name in the listbox are in vertical, and the element of the list are every single characters entered bu the user in the Input field. Furthermore, the elements will are overwritten when the user types another name in the inputbox.
This is the code
import PySimpleGUI as sg
from openpyxl import Workbook
from openpyxl import load_workbook
createColony_Layout=[]
firstWindow_Layout=[]
colonyList = []
# Software theme
# FIRST WINDOW
# Layouts
menuBar_Layout = [
['&File', ['&Inserisci file ICM Ctrl-O', '&Save Ctrl-S', '&Properties', 'E&xit']],
['&Edit', ['Undo']],
['&Toolbar', ['---', 'Command &1::Command_Key', 'Command &2', '---', 'Command &3', 'Command &4']],
['&Help', ['&About...']]
]
createColony_Layout = [
[sg.Text('Insert researcher name', size=20)],
[sg.Input(size=15, key='c')],
[sg.Button(button_text='Create', button_type=7)]
]
createColonyFrame = sg.Frame('Create new colony', createColony_Layout, size=(200, 100))
firstWindow_Layout = [
[sg.MenubarCustom(menuBar_Layout)],
[sg.Push(), sg.Text('Colony management',
justification=('Center'), font=('Helvetica', 30)), sg.Push()],
[createColonyFrame],
[sg.Listbox(colonyList, size =(50, 25), key='lista')]
]
# Create window
window = sg.Window('Colony management', firstWindow_Layout, size=(1300, 700),
auto_size_text= True, resizable=True, finalize=True)
window.TKroot.minsize(500,250)
#window.TKroot.maxsize(600, 700)
# Program loop
while True:
event,values = window.read()
if event == 'Create':
window['list'].update(values['c'])
if event == sg.WINDOW_CLOSED:
break
window.close()
And this is a screen of the window
I hope this is enough for you to help me, thank you.

The call to update for Listbox takes a list/tuple as the first argument. A string looks like a list of single characters in Python. To add a single value, make it a tuple instead of a string.
window['list'].update(values['c'])
becomes
window['lista'].update((values['c'],))
There is also a call to set the minimum size for a window that you can use instead of the TKroot access you've got in your code.
If you want a minimum size of (500, 250), then use this call:
window.set_min_size((500, 250))
import PySimpleGUI as sg
# from openpyxl import Workbook
# from openpyxl import load_workbook
createColony_Layout=[]
firstWindow_Layout=[]
colonyList = []
# Software theme
# FIRST WINDOW
# Layouts
menuBar_Layout = [
['&File', ['&Inserisci file ICM Ctrl-O', '&Save Ctrl-S', '&Properties', 'E&xit']],
['&Edit', ['Undo']],
['&Toolbar', ['---', 'Command &1::Command_Key', 'Command &2', '---', 'Command &3', 'Command &4']],
['&Help', ['&About...']]
]
createColony_Layout = [
[sg.Text('Insert researcher name', size=20)],
[sg.Input(size=15, key='c')],
[sg.Button(button_text='Create', button_type=7)]
]
createColonyFrame = sg.Frame('Create new colony', createColony_Layout, size=(200, 100))
firstWindow_Layout = [
[sg.MenubarCustom(menuBar_Layout)],
[sg.Push(), sg.Text('Colony management',
justification=('Center'), font=('Helvetica', 30)), sg.Push()],
[createColonyFrame],
[sg.Listbox(colonyList, size =(50, 25), key='lista')]
]
# Create window
window = sg.Window('Colony management', firstWindow_Layout, size=(1300, 700),
auto_size_text= True, resizable=True, finalize=True)
window.set_min_size((500, 250))
# window.TKroot.minsize(500,250)
#window.TKroot.maxsize(600, 700)
# Program loop
while True:
event,values = window.read()
if event == 'Create':
window['lista'].update((values['c'],))
if event == sg.WINDOW_CLOSED:
break
window.close()

Related

How to remove the option to select what's inside a text widget, (not by using state = disabled)

I have tried using exportselection = False
this is the code I use to get the input from the user, if the user is highlighting the text widget (while inputting their answer), they are able to edit where the input get's printed
import tkinter as tk
from tkinter import ttk
window = tk.Tk()
numb_of_times = 5
window.geometry('1920x1080')
window.configure(bg = 'blue')
input_board = tk.Text(window,state = "disabled")
input_board.pack()
input_board.place(x = 100,y = 40)
def send():
input_board.configure(state="normal")
input_board.insert(tk.INSERT, '%s\n' % user_input)
input_board.configure(state="disabled")
for i in range(numb_of_times):
user_input = input()
print(user_input)
send()
window.mainloop()
I have tried using exportselection = False
When the selection changes in the text widget, it emits a <<Selection>> event. You can bind to that event and remove the selection. This should prevent any text from being selected.
The selection is represented by the tag "sel", which you can pass to tag_remove.
The solution might look something like this:
def remove_selection(event):
event.widget.tag_remove("sel", "1.0", "end")
input_board.bind("<<Selection>>", remove_selection)

'Exception in tkinter callback' and '[Errno 24] Too many open files.'

quite new to Python and come across a problem that I'm struggling to diagnose.
I am working on a project involving a Tkinter menu and using an ADC connected to a potentiometer (which will hopefully be replaced by a muscle sensor once I've gotten the essential code working). I am using a raspberry Pi and breadboard for this, with my IDE as Thonny.
The imports are:
from time import sleep
import busio
import digitalio
import board
import adafruit_mcp3xxx.mcp3008 as ADC
from adafruit_mcp3xxx.analog_in import AnalogIn
import tkinter as tk
from tkinter import Menu
import easygui
import RPi.GPIO as GPIO
The overall menu and code works until I try to use the Run() function, which uses all of the files and compares AnalogIn to a threshold value previously found. When the Run button is clicked, it comes up with the error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.9/tkinter/__init__.py", line 1892, in __call__
File "/home/pi/Documents/NEA_Python/NEA2023.py", line 280, in Run
File "/usr/local/lib/python3.9/dist-packages/adafruit_mcp3xxx/analog_in.py", line 53, in value
File "/usr/local/lib/python3.9/dist-packages/adafruit_mcp3xxx/mcp3xxx.py", line 80, in read
File "/usr/local/lib/python3.9/dist-packages/adafruit_bus_device/spi_device.py", line 93, in __enter__
File "/usr/local/lib/python3.9/dist-packages/busio.py", line 335, in configure
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 650, in any_embedded_linux
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 532, in any_raspberry_pi
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/board.py", line 205, in _pi_rev_code
File "/usr/local/lib/python3.9/dist-packages/adafruit_platformdetect/__init__.py", line 42, in get_cpuinfo_field
OSError: [Errno 24] Too many open files: '/proc/cpuinfo'
Run() Function code:
def Run():
threshold_valid = False
output_valid = False
#validity checks for all necessary fields
if threshold_valid == False:
thres = open('Threshold.txt', 'r')
threshold = thres.read()
if threshold == '':
print("Missing threshold voltage. Please refer to Main Menu/Options/Edit Muscle Data/Find threshold voltage")
else:
threshold_valid = True
if output_valid == False:
output = open('OutputOption.txt', 'r')
outputOption = output.read()
if outputOption == '':
print("Missing output option. Please refer to Main Menu/Options/Edit Muscle Data/Choose output/ in order to select output")
elif outputOption == 'notification':
with open('Message.txt', 'r') as message:
if message.read() == '':
print("Missing message to display. Please refer to Main Menu/Options/Edit Muscle Data/Choose output/Display message/")
else:
output_valid = True
message.close()
elif outputOption() == 'LED':
output_valid = True
else:
print("Something went wrong. Try deleting stored data at Main Menu/Options/Edit Muscle Data/Delete Data/ and try again")
while threshold_valid == True and output_valid == True:
spiBus = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
# create the chip select
chipSelect = digitalio.DigitalInOut(board.D22)
#creates the object for adc
adc = ADC.MCP3008(spiBus, chipSelect)
#creates analogue input channel on ADC's pin 0
analogueInChan = AnalogIn(adc, ADC.P0)
instantaneousAIn = analogueInChan.value
if instantaneousAIn> int(threshold):
if outputOption == 'LED':
output_flashLED()
elif outputOption == 'notification':
output_notification()
thres.close()
output.close()
All of the validity checks for this work fine, but it fails once it reaches the part inside the 'while threshold_valid == True and output_valid == True:' statement.
The menu is a menubar with cascade menus coming off of it.
The Tkinter menu code is:
#Root window
root = tk.Tk()
root.title("Main Menu")
#Creates a menubar
menubar = Menu(root, bg = 'powderblue', activebackground = '#84b7c4', tearoff = 0)
root.config(menu = menubar)
#Creates the main menu with the menubar as its parent window
#bg is the natural background colour, activebackground is the colour when the mouse hovers over it
main_menu = Menu(menubar, bg = 'lightcyan', activebackground = 'powderblue', tearoff = 0)
#Adds option to main_menu
main_menu.add_command(
label = "Instructions",
command = instructions
)
main_menu.add_command(
label= "Run",
command = Run
)
#Adds the main_menu as cascade to the menubar
menubar.add_cascade(
label = "Options",
menu = main_menu
)
#Creates menu muscleData_menu with main_menu as parent window
muscleData_menu = Menu(main_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to muscleData_menu
muscleData_menu.add_command(
label = "Instructions",
command = instructions
)
muscleData_menu.add_command(
label = "Find threshold voltage",
command = findThreshold
)
#Creates menu output_menu with muscleData_menu as parent window
output_menu = Menu(muscleData_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to output_menu
output_menu.add_command(
label = "Flash LED",
command = menuoption_flashLED
)
output_menu.add_command(
label = "Display message",
command = menuoption_notification
)
#Adds output_menu as cascade within muscleData_menu
muscleData_menu.add_cascade(
label = "Choose output",
menu = output_menu
)
#Creates menu deleteData_menu with muscleData_menu as parent window
deleteData_menu = Menu(muscleData_menu, tearoff = 0, bg = 'lightcyan', activebackground = 'powderblue')
#Adds option to deleteData_menu
deleteData_menu.add_command(
label = "Delete stored threshold voltage",
command = deleteThreshold
)
deleteData_menu.add_command(
label = "Delete stored output option",
command = deleteOutput
)
deleteData_menu.add_command(
label = "Delete stored notification message",
command = deleteMessage
)
deleteData_menu.add_command(
label = "Delete all stored data",
command = wipeData
)
#adds deleteData_menu as cascade to the muscleData_menu
muscleData_menu.add_cascade(
label = "Delete stored data",
menu = deleteData_menu
)
muscleData_menu.add_command(
label = "Run",
command = Run
)
#Adds muscleData_menu as cascade within main_menu
main_menu.add_cascade(
label = "Edit Muscle Data",
menu = muscleData_menu,
)
#Ends menu program
#root.destroy completely erases root/menu
main_menu.add_command(
label = "Quit",
command=root.destroy,
)
#Runs menu continuously until quit, awaiting user inputs
root.mainloop()
The actual menu works fine, the issue resides inside of the Run() function, however it is a Tkinter related issue. As I understand it, the menu works by the root looping continuously in the background whilst allowing other parts of the code to work, so this may be the issue but I'm not sure how to fix it. I have checked and in all instances of me opening the textfiles across the code, I have closed them again directly after. I have changed the ulimit and it continues to show the same error. I couldn't find a working solution in other stackoverflow answers and so believed it was an issue more localised to my exact code, but apologies if this is a repetition.
The full code is in this google doc, if anyone finds it relevant:
https://docs.google.com/document/d/1Oj5M6jSTXepXSBm9kjj4o_41AN4IhGcgc63iedZSMdQ/edit?usp=sharing
Can you limit the number of files that have to be open at the same time?
I was also reading from this thread,
IOError: [Errno 24] Too many open files:
Please have a look , it might help.
You need to use a with block to deal with your files, so they actually get closed and you won't run out of file descriptors.
Replace things like
thres = open('Threshold.txt', 'r')
threshold = thres.read()
with
with open('Threshold.txt', 'r') as f:
threshold = f.read()

How to convert user voice input into text and store in the excel sheet?

Dear All,
I wrote a simple program of data entry into excel using python.
But I task is that this manual entry will be performed from user voice input.
Can anybody help me in this regard.
import PySimpleGUI as sg
import pandas as pd
import speech_recognition as sr
# Add some color to the window
sg.theme('Green')
exfile = 'Break Down Report.xlsx'
df = pd.read_excel(exfile)
layout = [ # for input text
[sg.Text('Machines Breakdown Entries Table:')],
[sg.Text('S.No', size=(15,1)), sg.InputText(key='S.No')],
[sg.Text('Fault Desc', size=(15,1)), sg.InputText(key='Fault Desc')],
[sg.Text('Start Time', size=(15,1)), sg.InputText(key='Start Time')],
[sg.Text('End Time', size=(15,1)), sg.InputText(key='End Time')],
[sg.Text('Date', size=(15,1)), sg.InputText(key='Date')],
[sg.Text('Employe Name', size=(15,1)), sg.InputText(key='Employe Name')],
# for buttons
[sg.Button('Speak'), sg.Save(), sg.Button('Clear'), sg.Quit()]
]
# main tool bar Window text
window = sg.Window('Daily Breakdown Report ', layout)
def clear_input():
for key in values:
window[key]('')
return None
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Quit':
break
if event == 'Clear':
clear_input()
if event == 'Save':
df = df.append(values, ignore_index=True)
df.to_excel(exfile, index=False)
sg.popup('Data saved!')
clear_input()
window.close()
I want to develop a program in Python that performs voice activity in an excel sheet, similar to Google Docs.
According to above code. Manual entries will be performed by the user typing. But i want create a voice button in which user will command by voice and input should store into text and save in excel sheet.
Using thread to do the conversion, like the method window.perform_long_operation.
Demo Code
import speech_recognition as sr
import PySimpleGUI as sg
def speak():
while True:
r = sr.Recognizer()
text = '~ Not recongnized ~'
with sr.Microphone() as source:
audio = r.listen(source)
try:
text = r.recognize_google(audio,language="en-IN")
except:
pass
return text
layout = [
[sg.Multiline(size=(80, 10), autoscroll=True, key='-ML-')],
[sg.Push(), sg.Button('Speak')],
]
window = sg.Window('Title', layout)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event == 'Speak':
window['Speak'].update(disabled=True)
window.perform_long_operation(speak, 'Done')
elif event == 'Done':
text = values['Done']
window['Speak'].update(disabled=False)
window['-ML-'].update(text+'\n', append=True)
window.close()

display the CMD console in a window in GUI - PySimpleGUI

I'm creating a GUI using PySimpleGUI and want to have a frame that displays the output prints of all the functions in the program (as in the cmd console).
I tried using (relevant code lines):
def hello():
print('hello')
layout = [[sg.Frame("Output console", [[sg.Text(" ", size=(0, 1), key='-OUTPUT-')]])]]
window['-OUTPUT-'].update(hello())
I can see the print in the console but not in the Frame in the GUI.
How can i do that?
To show the output like in CMD console, you should use the sg.Multiline, not the sg.Text which is just a label.
To redirect the output of stdout and stderr, set options reroute_stdout=True and reroute_stderr=True, also set option autoscroll=True to automatically scroll as more data added to the end of element.
Demo Code - GUI will update only back to window.read
import PySimpleGUI as sg
def hello():
print('hello')
frame_layout = [[sg.Multiline("", size=(80, 20), autoscroll=True,
reroute_stdout=True, reroute_stderr=True, key='-OUTPUT-')]]
layout = [
[sg.Frame("Output console", frame_layout)],
[sg.Push(), sg.Button("Say Hello")],
]
window = sg.Window("Title", layout, finalize=True)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "Say Hello":
hello()
window.close()
To update GUI in realtime, you can call update(value, append=True) and window.refresh, not to use the print statement, or
Following code show the way to print to sg.multiline immediately. To avoid stop GUI working for long printing, mutlithread required here. It will not work for real time printing during event loop because loop is working on your printing statement, then back to window.read after it done.
It is much complex, not sure if any bug.
import sys
from time import sleep
from datetime import datetime
import threading
import PySimpleGUI as sg
def print_task():
global printing
while printing:
print(datetime.now().strftime("%H-%M-%S"), datetime.now().strftime("%H-%M-%S"))
sleep(0.5)
class Unbuffered(object):
def __init__(self, window):
self.window = window
def write(self, data):
self.window.write_event_value("OUT", data)
def writelines(self, datas):
self.window.write_event_value("OUT", ''.join(datas))
frame_layout = [[sg.Multiline("", size=(80, 20), autoscroll=True, key='-OUTPUT-')]]
layout = [
[sg.Frame("Output console", frame_layout)],
[sg.Push(), sg.Button("Print"), sg.Button('Stop')],
]
window = sg.Window("Title", layout, finalize=True)
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout = Unbuffered(window)
sys.stderr = Unbuffered(window)
printing = False
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'Print':
if not printing:
printing = True
threading.Thread(target=print_task, daemon=True).start()
elif event == 'Stop':
printing = False
elif event == "OUT":
window['-OUTPUT-'].update(values["OUT"], append=True)
printing = False
sys.stdout, sys.stderr = old_stdout, old_stderr
window.close()

Pysimplegui help, I want to rerun my process over and over

I'm still learning python and I am giving myself a project which I am iterating on. I create some random game selectors using CSV files or text files. So I did some research and made a simple gui using pysimplegui and it works. However I want to rerun the script to select a new game with I hit the reload button and I'm just not finding the information I looking for. Maybe I am overthinking it, or underthinking it.
import random
import csv
import PySimpleGUI as sg
#select Random game from CSV file
with open("Retro Games Spreadsheet! - Games!.csv") as f:
reader = csv.reader(f)
chosen_row = random.choice(list(reader))
f.close()
print(chosen_row)
#Create the layout
layout = [[sg.Text(chosen_row)], sg.Button("Reload")],
# Create the window
window = sg.Window("Select Random Game", layout)
# Create an event loop
while True:
event, values = window.read()
# End program if user closes window or
# reloads with the Reload button
if event == sg.WIN_CLOSED:
break
elif event == "Reload":
window[[sg.Text(chosen_row)]]
window.Refresh()
window.close()
Just update all related elements in your layout.
import random
import csv
import PySimpleGUI as sg
#select Random game from CSV file
with open("Retro Games Spreadsheet! - Games!.csv") as f:
reader = csv.reader(f)
rows = list(reader)
chosen_row = random.choice(rows)
#f.close() it will be auto-closed after with statement
print(chosen_row)
#Create the layout
layout = [[sg.Text(chosen_row, key="-ROW-")], sg.Button("Reload")],
# Create the window
window = sg.Window("Select Random Game", layout)
# Create an event loop
while True:
event, values = window.read()
# End program if user closes window or
# reloads with the Reload button
if event == sg.WIN_CLOSED:
break
elif event == "Reload":
chosen_row = random.choice(rows)
window['-ROW-'].update(chosen_row)
window.close()

Resources