How to show images in window in PySimpleGUI? - python

I'm working on PySimpleGUI and I want to show images in the Window of PySimpleGUi. Basically, first I'm converting my sentence into characters and now I want to simply show an image of that character in the window and speak which is the character.
Below is my code:
from tkinter.constants import TRUE
import PySimpleGUI as sg
from PySimpleGUI.PySimpleGUI import Window
import pyttsx3
import time
import _thread
first_coloumn = [
[sg.LBox([], size=(20,10), key='-FILESLB-')],
[sg.Input(visible=False, enable_events=True, key='-IN-'), sg.FilesBrowse()],
[sg.Text('Enter what you wanna teach : ')],
[sg.Input(key='-inp-')],
[sg.Button('Reads'),sg.Button('Pause'),sg.Button('Slow'),sg.Button('Fast'),sg.Button('Stop'),sg.Button('Repeat'),sg.Button('Exit')]
]
image_viewer_coloumn = [
[sg.Text("Image Viewer")],
[sg.Image(key='-IMAGE-')],
]
layout = [
[
sg.Column(first_coloumn),
sg.VSeparator(),
sg.Column(image_viewer_coloumn),
]
]
window = sg.Window("Narrator",layout)
engine = pyttsx3.init()
words = []
chars = []
a = 0.5
stop = False
def splits(sentence):
return list(sentence)
def speak(a):
global stop
for i in range (len(chars)):
if stop:
break
elif chars[i]=='A' or chars[i]=='a':
filepath = "D:\Office-Work\Chars\A.png"
window['-IMAGE-'].update(filepath=filepath)
engine.say(chars[i])
time.sleep(a)
engine.runAndWait()
while TRUE:
event,values = window.read()
if event == 'Reads':
out = values['-inp-']
if out:
chars = splits(out)
stop = False
_thread.start_new_thread(speak, (a,))
and I'm getting the following error:
update() got an unexpected keyword argument 'filepath'
File "C:\Users\Kashi\OneDrive\Desktop\PySimpleGUI\a.py", line 45, in speak
window['-IMAGE-'].update(filepath=filepath)

sg.Image defined as
def __init__(self, source=None, filename=None, data=None, background_color=None, size=(None, None), s=(None, None), pad=None, p=None, key=None, k=None, tooltip=None, right_click_menu=None, expand_x=False, expand_y=False, visible=True, enable_events=False, metadata=None):
You can find there's no option for filepath, another issue is not to update GUI when not in main thread.
...
elif chars[i]=='A' or chars[i]=='a':
filepath = "D:\Office-Work\Chars\A.png"
"""
window['-IMAGE-'].update(filepath=filepath) # unexpected keyword argument 'filepath'
window['-IMAGE-'].update(filename=filepath) # correct keyword, but not update GUI here
"""
window.write_event_value("Update Image", filepath) # Generate an event when not in main thread.
...
while TRUE:
event,values = window.read()
...
if event == "Update Image":
window['-IMAGE-'].update(filename=filepath)
...
Should not update GUI in other thread function speak.

Related

Python: How to retrieve pressed characters in wxPython ListBox respecting keyboard layout?

In Python 3.10.5 on Windows 11 employing the wxPython 4.2.0 package, I have created a wx.ListBox widget. Now I want to listen to key presses when the list box is focused. But I need the character codes returned by the key press handler to respect the current keyboard layout, that is, for instance, whenever I press with the Czech keyboard layout the keys in the second key row that produce numbers 1, 2, 3, etc. in the English keyboard layout, I need not to receive character codes which when converted to characters using the chr() Python function result in numbers 1, 2, 3, etc., but instead I need to receive unicode char codes which when converted with chr() result in the Czech accented characters ě, š, č, etc.
The code snippet I am using so far is the following:
listbox = wx.ListBox(panel, size=(1200, 500), choices=[], style=wx.LB_SINGLE)
listbox.Bind(wx.EVT_CHAR_HOOK, onListboxCharHook)
...
def onListboxCharHook(self, event):
unicodeKey = event.GetUnicodeKey()
print(chr(unicodeKey))
In other words, the code above prints 1, 2, 3, 4, etc. when keys in the number row are pressed, but instead I need +, ě, š, č, etc. if the Czech keyboard layout is active.
I've also tried using EVT_KEY_DOWN event type instead of EVT_CHAR_HOOK, but the result is the same. Note that the wx.TextCtrl has a EVT_CHAR event type. When this event type is used, the chr(event.GetUnicodeKey()) correctly returns the characterss respecting the current keyboard layout as I need. But the problem is that wx.ListBox does not support the EVT_CHAR event type, so I am not able to get those actually pressed characters using wx.ListBox.
Therefore, is there some way using the wx.ListBox widget to translate the EVT_CHAR_HOOK event key codes into the actually pressed characters respecting the current keyboard layout?
Thanks to the answer to a similar question - Detect keyboard input with support of other languages from English, I have put together a working code which on key press prints the pressed character respecting the current keyboard layout as intended by the question. The releveant parts of that code are the following.
import wx
listbox = wx.ListBox(panel, size=(1200, 500), choices=[], style=wx.LB_SINGLE)
listbox.Bind(wx.EVT_KEY_DOWN, self.onListboxKeyDown)
def onListboxKeyDown(self, event):
rawKey = event.GetRawKeyCode()
unicodeKey = event.GetUnicodeKey()
modifiers = event.GetModifiers()
onlyShiftDown = modifiers == (wx.MOD_SHIFT)
# Handle the press of any character key without any or only with the Shift modifier
if (unicodeKey != wx.WXK_NONE) and (not modifiers or onlyShiftDown):
char = getCurrentLayoutChar(rawKey, onlyShiftDown)
print(char)
return
event.Skip()
import os
import sys
### Translate the specified virtual-key code and keyboard state
# to the corresponding Unicode character or characters.
# learn.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-tounicodeex
### Adapted from
# the solution to https://stackoverflow.com/questions/38224277/
# by https://stackoverflow.com/users/235698/mark-tolonen
###
from ctypes import (
WinDLL, POINTER, create_string_buffer, create_unicode_buffer,
c_int32, c_uint, c_uint, c_char, c_wchar, c_int, c_uint, c_void_p
)
_ToUnicodeEx = WinDLL('user32').ToUnicodeEx
_ToUnicodeEx.argtypes = [
c_uint, # wVirtKey virtual-key code to be translated
c_uint, # wScanCode hardware scan code of ˙wVirtKey˙
POINTER(c_char), # lpKeyState current keyboard state (256-byte array)
POINTER(c_wchar), # pwszBuff buffer that receives translated chars
c_int, # cchBuff size of the `pwszBuff` buffer (in chars)
c_uint, # wFlags behavior of the function
c_void_p # dwhkl input locale identifier
]
_ToUnicodeEx.restype = c_int
def ToUn(vk,sc,wfl,hkid, shiftDown=False):
kst = create_string_buffer(256)
if shiftDown:
kst[16] = 0x80
b = create_unicode_buffer(5)
if _ToUnicodeEx(vk,sc,kst,b,5,wfl,hkid):
return b.value
else:
return chr( 0xFFFD) # Replacement Character
### Retrieve the active input locale identifier
# (formerly called the keyboard layout)
# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardlayout
###
# Method based on my own research; non-optimized, debugged on Windows 10…
###
from ctypes import WinDLL
user32 = WinDLL('user32', use_last_error=True)
def get_servant_conhost(pid, proclist):
"""Find “attendant” host process (conhost.exe)"""
aux = [_ for _ in proclist if _[0] == pid]
if len( aux) > 0:
auxcon = [x for x in proclist if (
x[1] == aux[0][0] and x[2] == "conhost.exe")]
if len( auxcon) == 0:
auxconret = get_servant_conhost(aux[0][1], proclist)
return auxconret
else:
auxconret = auxcon[0]
auxconret.append( aux[0][2])
return auxconret
else:
return []
def get_conhost_threads():
if sys.executable.lower().endswith('\\pythonw.exe'):
return []
import wmi
c = wmi.WMI()
w_where = ' or '.join([
'Name like "p%.exe"', # py.exe|python.exe|pwsh.exe|powershell.exe
'Name = "conhost.exe"',
'Name = "cmd.exe"'
])
w_properties = 'ProcessId, ParentProcessId, Name'
w_wql = f'SELECT {w_properties} FROM Win32_Process where {w_where}'
w_wqlaux = c.query(w_wql)
proc_list = [[wqlitem.__getattr__('ProcessId'),
wqlitem.__getattr__('ParentProcessId'),
wqlitem.__getattr__('Name')] for wqlitem in w_wqlaux]
servant_conhost = get_servant_conhost(os.getpid(), proc_list)
if len( servant_conhost) == 0:
return []
else:
try:
w_where = f'ProcessHandle = {servant_conhost[0]}'
w_wql = f'SELECT Handle FROM Win32_Thread WHERE {w_where}'
w_wqlHandle = c.query(w_wql)
wqlthreads = [x.__getattr__('Handle') for x in w_wqlHandle]
except:
wqlthreads = []
return wqlthreads
# required if run from `cmd` or from the `Run` dialog box (`<WinKey>+R`)
conhost_threads = get_conhost_threads()
def get_current_keyboard_layout():
foregroundWindow = user32.GetForegroundWindow();
foregroundProcess = user32.GetWindowThreadProcessId(int(foregroundWindow), 0);
keyboardLayout = user32.GetKeyboardLayout(int(foregroundProcess));
keyboardLayout0 = user32.GetKeyboardLayout(int(0));
if keyboardLayout == 0 or len(conhost_threads):
if keyboardLayout == 0:
keyboardLayout = keyboardLayout0
for thread in conhost_threads:
aux = user32.GetKeyboardLayout( int(thread))
if aux != 0 and aux != keyboardLayout0:
keyboardLayout = aux
break
return keyboardLayout
def getCurrentLayoutChar(key, shiftDown):
c_hkl = get_current_keyboard_layout()
char = ToUn(key, 0, 0,c_hkl, shiftDown)
return char

How to add dividers to rumps menu

I have made a python script which creates a MacOS Status bar item which displays youtube statistics.
I want to add dividers to the drop down menu when you click the text but I am unable to do this. (Image of what I mean under the text). I have found many examples but all of them only work with an __init__ function in the class. If I try adding an __init__ function to the class I get an error saying AttributeError: 'Sub_Counter' object has no attribute '_menu'. Why is this happening and how can it be fixed?
Code I added to the __init_ function
self.menu = [
"About",
"No Icon",
None,
"Detailed Statistics:",
None,
"Quit",
]
Normal Code without the __init__ function
import rumps
import time
import sys
import os
from sty import fg
from googleapiclient.discovery import build
key = open(os.path.join(sys.path[0], './key.txt')).read().strip()
service = build('youtube', 'v3', developerKey=key)
subs = service.channels().list(
part='statistics',
id='UCERizKQbgpBXOck0R6t_--Q'
).execute()['items'][0]['statistics']['subscriberCount']
timers = ["1 secs","5 secs","10 secs","15 secs","20 secs","25 secs","30 secs","35 secs","45 secs","50 secs","1 Min"]
EXEC_TIMER = 60
class Sub_Counter(rumps.App):
#rumps.timer(EXEC_TIMER)
def pull_data(self, _):
self.sub_menu = timers
subs = service.channels().list(
part='statistics',
id='UCERizKQbgpBXOck0R6t_--Q'
).execute()['items'][0]['statistics']['subscriberCount']
a = (str(subs))
self.icon = "logo.png"
self.title = "Subscribers: " + str(a)
self.notification = str(a) + " Subscribers"
#rumps.clicked("About")
def about(self, _=):
rumps.notification("Youtube Subscriber Count", "Made by Roxiun using Python & rumps", "Shows Youtube Subscriber counts")
#rumps.clicked("No Icon")
def noicon(self, sender):
sender.state = not sender.state
self.icon = None
#rumps.clicked("Detailed Statistics")
def Detailed_Statistics(self, _):
rumps.notification("You have:", self.notification , "Veiws Comming Soon")
if __name__ == "__main__":
Sub_Counter("Loading...").run() #debug=True
Image of what I want to do [circled in red - (Yes it is the line)]
Thanks in advance!
Fixed by doing
app = Sub_Counter("Loading...")
app.menu[
"About",
"No Icon",
None,
"Detailed Statistics:",
None,
"Quit",
]
app.run()
You can add a separator by doing:
self.menu.add(rumps.separator)
Link to source code.

How To Read File Input In pySimpleGUI Then Pass It On To A Number-Crunching Processor

I would like to take input from pySimpleGUI, feed it into a normal Python var, then feed it into a music processor as I love music.
I had already tried to use wxPython for this but was unable to even get a simple fileDialog without crashing.
from pydub import AudioSegment
from os import listdir
import numpy as np
import math
import PySimpleGUI as sg
class Dankify():
song_dir = "songs"
attenuate_db = 0
accentuate_db = 2
yeet = sg.Window('Dankify ALL THE THINGS!'). Layout([[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()] ]).Read()
event, values = yeet.Read()
yeet1 = event, values
def bass_line_freq(track):
sample_track = list(track)
# c-value
est_mean = np.mean(sample_track)
# a-value
est_std = 3 * np.std(sample_track) / (math.sqrt(2))
bass_factor = int(round((est_std - est_mean) * 0.005))
return bass_factor
songfile = yeet1
for filename in listdir(songfile):
sample = AudioSegment.from_mp3(songfile)
filtered = sample.low_pass_filter(bass_line_freq(sample.get_array_of_samples()))
combined = (sample - attenuate_db).overlay(filtered + accentuate_db)
combined.export("exports/" + filename.replace(".mp3", "") + "-export.mp3", format="mp3")
However, it just does nothing, not even processing it. A reminder that I am using some open-source code and that I'm a beginner which knows nothing about how all this works and am trying to build real stuff to gain experience. Thanks!
I guess you are missing the "event loop".
Try something like this, hope it helps.
import sys
if sys.version_info[0] >= 3:
import PySimpleGUI as sg
else:
import PySimpleGUI27 as sg
layout = [[sg.Text('Your typed chars appear here:'), sg.Text('', key='_OUTPUT_') ],
[sg.Input(do_not_clear=True, key='_IN_')],
[sg.Button('Show'), sg.Button('Exit')]]
window = sg.Window('Window Title').Layout(layout)
while True: # Event Loop
event, values = window.Read()
print(event, values)
if event is None or event == 'Exit':
break
if event == 'Show':
# change the "output" element to be the value of "input" element
window.FindElement('_OUTPUT_').Update(values['_IN_'])
window.Close()
You're doing 2 Read calls.
Try changing to this:
yeet = sg.Window('Dankify ALL THE THINGS!').Layout(
[[sg.Text('Filename')], [sg.Input(), sg.FileBrowse()], [sg.OK(), sg.Cancel()]])
event, values = yeet.Read()
Without the Read on the end of the first statement.
You are instantiating this class, right?
d = Dankify()

RuntimeError: deleteUI: Object 'Animation_Copy_Tool' not found

Getting this error when pressing the "Close Window" button in my UI. This button should delete the UI window but isn't. Full traceback:
Error: deleteUI: Object 'Animation_Copy_Tool' not found.
Traceback (most recent call last):
File "", line 36, in closeBtnCmd
RuntimeError: deleteUI: Object 'Animation_Copy_Tool' not found. #
# Animation Copy Tool
# Bakari Holmes 5/7/2015
# This is designed to copy and existing animation
# from one rig to another and make the process easier
# with a simple UI
import maya.cmds as mc
import functools
import maya.mel as mm
import pprint
class AnimCopyWindow(object):
##classmethod
def showUI(cls):
win = cls()
win.create()
return win
def __init__(self):
self.window = "Animation Copy Tool"
self.title = "Animation Copier"
self.size = (546,350)
def pasteTheseKeys(self, *args):
self.offsetVal = mc.intFieldGrp(self.int_offset, q=True, value1=True)
self.selObj_pasteKeys = mc.ls(sl=True)
for objectQuant in self.selObj_pasteKeys:
print objectQuant
self.ct = mc.currentTime(query = True)
self.t = self.ct + self.offsetVal
mc.currentTime(self.t)
# mc.selectKey(selObj_pasteKeys[objectQuant])
mc.pasteKey(time=(self.t,self.t), f=(1.0,1.0), option="merge", copies=1, to=0, fo=0, vo=0)
def closeBtnCmd(self,*args):
mc.deleteUI(self.window,window=True)
def create(self):
# check to see if window exists already
if mc.window(self.window,exists=True):
mc.deleteUI(self.window,window=True)
self.window = mc.window(self.window, title=self.title,widthHeight=self.size,menuBar=True)
self.copyAnim = mc.window(title="Transfer Animation Tool", backgroundColor=[0.3,0.3,0.3],sizeable=False,resizeToFitChildren=True)
#set the layout for UI
mc.columnLayout(adjustableColumn=True)
self.tx_src = mc.textFieldGrp(label="Source Object", editable=False, text=sel[0])
self.int_offset = mc.intFieldGrp(label="Frame Offset Amount", value1=0)
#add paste animation button
self.btn1 = mc.button(label="PASTE ANIMATION", command=self.pasteTheseKeys, bgc=[0.1,0.1,0.5])
#add close button window
self.btn2 = mc.button(label="CLOSE WINDOW", command=self.closeBtnCmd, bgc=[0.2,0.2,0.2])
mc.showWindow()
#################################
#####end of class definition#####
#################################
def keys_as_dictionary(channel):
"""return a dictionay of times:values for <channel>"""
keys = mc.keyframe(channel, q=True, tc=True) or []
values = mc.keyframe(channel, q=True, vc=True) or []
return dict(zip(keys, values))
def channels():
"""return a dictionary of <plug>:<channel_dict> for each animated plug selected"""
keys = mc.keyframe(sl=True, n=True, q=True)
result = {}
for k in keys:
plugs = mc.listConnections(k, p=True)[0]
result[plugs]= keys_as_dictionary(k)
return result
#store selected object info
sel = mc.ls(selection=True)
if (len(sel) != 1):
mm.eval("warning Must select one animated object;")
else:
mc.copyKey()
win = AnimCopyWindow()
win.create()
pprint.pprint(channels())
This error almost always means your UI element is not named what you think it is: Maya will automatically rename the items to make sure that no two siblings have the same name -- you can ask for "my_window" and get back "my_window123" . So you need to capture the actual name that is returned from cmds.window() or whatever ui command you use and delete that. Hard coded names are never reliable

Python: Can't pop from an empty list

I am creating a python program to detect and enable usb to usb data transfer between usb storage drives. However I am having an issue with updating the dev_label (device name of the drive) and passing it to Exchange. Here is the code :
serial_list=[]
context = Context()
monitor = Monitor.from_netlink(context)
monitor.filter_by(subsystem='block',device_type='partition')
observer = GUDevMonitorObserver(monitor)
def device_connected(observer, device):
Welcome.device_count+=1
flag =False
for iden in serial_list :
if iden == device.__getitem__('ID_SERIAL_SHORT'):
flag=True
if flag ==False:
serial_list.append(device.__getitem__('ID_SERIAL_SHORT'))
Welcome.dev_label.append(str(device.__getitem__('ID_FS_LABEL')))
size = len(Welcome.dev_label)
label = gtk.Label('Device connected :: {0!r}'.format(Welcome.dev_label[size-1]))
Welcome.vbox.pack_start(label)
Welcome.window.show_all()
if Welcome.device_count<2:
label = gtk.Label('Connect the second device')
Welcome.vbox.pack_start(label)
Welcome.window.show_all()
else :
Exchange()
observer.connect("device-added",device_connected)
monitor.start()
class Welcome:
device_count = 0
window = gtk.Window()
vbox= gtk.VBox(False, 5)
dev_label = []
def __init__(self):
self.window.set_default_size(300, 300)
self.window.set_title("Welcome")
label = gtk.Label("Connect the desired device")
self.vbox.pack_start(label)
self.window.add(self.vbox)
self.window.connect("destroy", lambda q: gtk.main_quit())
self.window.show_all()
class Exchange:
window1 = gtk.Window(Welcome.dev_label.pop())
window2 = gtk.Window(Welcome.dev_label.pop())
def __init__(self):
width = gtk.gdk.screen_get_default().get_width()
height = gtk.gdk.screen_get_default().get_height()
self.window1.resize(width/2,height)
self.window2.resize(width/2,height)
self.window2.move(self.window1.get_position()[0]+width/2, self.window1.get_position()[1])
label = gtk.Label("Hello")
self.window1.add(label)
self.window1.connect("destroy" , lambda q : gtk.main_quit())
self.window1.show_all()
label = gtk.Label("World")
self.window2.add(label)
self.window2.connect("destroy",lambda q : gtk.main_quit())
self.window2.show_all()
Welcome()
gtk.main()
The error shown in the trace back is :
Traceback (most recent call last):
File "project.py", line 70, in <module>
class Exchange:
File "project.py", line 71, in Exchange
window1 = gtk.Window(Welcome.dev_label.pop())
IndexError: pop from empty list
I can't figure out how to synchronize all these event so that the compiler doesn't throw an error. Values are being popped from Welcome.dev_label only after they've been updated in device_connected so why does the compiler have a problem? I am a python newbie so please be gentle.
This is not the compiler givin errors but the program.
You can change your class to this:
import time
class Exchange:
while not Welcome.dev_label: time.sleep(0.001)
window1 = gtk.Window(Welcome.dev_label.pop()) # line 4
while not Welcome.dev_label: time.sleep(0.001)
window2 = gtk.Window(Welcome.dev_label.pop())
This would be kind of a synchronization primitive given that only line 4 and 6 remove content.
In general you would use a queue for this.
import queue # Python3 # import Queue Python 2
Welcome.dev_label # = queue.Queue()
Welcome.dev_label.put(...) # instead of append
Welcome.dev_label.get(...) # instead of pop
But I do not know wether your code uses threads and runs in parallel. If the time.sleep example works then you can switch to a queue.

Categories