Python: How to show latest thread in a GUI - python

I am using a GUI to display a countdown timer which will be activated when user click start countdown.
Using another thread to compute the countdown timer , however i only require the latest thread to write on the display, instead of multiple thread writing on the display.
*Button can be clicked multiple time(or used to reset the countdown), just need to show the newest thread data.
import time
import logging
import threading
import PySimpleGUI as sg
cp = sg.cprint
def main():
sg.theme('DarkAmber')
col1 = [[sg.Button('start countdown', button_color='white on green', size=(18, 5), font=('Arial', 30, 'bold'))],
[sg.Button('Buzzer Stop', button_color='red', font=('Arial', 30, 'bold'), size=(18, 5))
]]
col2 = [[sg.Multiline(size=(100, 2), key='-AB-', font=('Arial', 50, 'bold'), reroute_stdout=True, write_only=True,
reroute_cprint=False)],
[sg.Multiline(size=(100, 6), key='-ML-', autoscroll=True, reroute_stdout=True, write_only=True,
reroute_cprint=True)]
]
layout_test = [[sg.Column(col1, element_justification='c'), sg.Column(col2, element_justification='c')]]
window = sg.Window('Counter', layout_test, size=(800, 480))
while True:
event, values = window.read()
cp(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
if event.startswith('start countdown'):
t = 900
t1 = threading.Thread(target=countdown, args=(t, window), daemon=True).start()
if event.startswith('Buzzer Stop'):
threading.Thread(target=Samplingthread, args=(window,), daemon=True).start()
Buzzerflag = 60
window.close()
def countdown(t, window):
while t:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
window['-AB-'].print(timer)
time.sleep(1)
t -= 1
if __name__ == '__main__':
main()

Not to update GUI in your thread.
from time import sleep
import threading
import PySimpleGUI as sg
def count_down(window):
count = 100
while count and countdown:
window.write_event_value("COUNT", count)
count -= 1
sleep(1)
def count_up(window):
count = 0
while not countdown:
window.write_event_value("COUNT", count) # CANNOT update GUI in thread
count += 1
sleep(1)
sg.theme('DarkBlue3')
layout = [
[sg.Button('COUNTDOWN'), sg.Button('TIMER')],
[sg.Text('', size=(20, 1), key='LABEL')],
]
window = sg.Window('TITLE', layout, finalize=True)
label = window['LABEL']
countdown = False
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'COUNTDOWN':
countdown = True
threading.Thread(target=count_down, args=(window,), daemon=True).start()
elif event == 'TIMER':
countdown = False
threading.Thread(target=count_up, args=(window,), daemon=True).start()
elif event == 'COUNT':
label.update(value=values[event])
countdown = not countdown
sleep(1)
window.close()

Related

How can I use a single button to pause and unpause timer in tkinter? (without using pygame)

I'm working on a simple timer that counts down 30 mins for me to study and 5 mins for a break. So far, the start_timer function and count_down function work well, I just cannot figure out how to write the pause function. I did some research for a few days. Most articles are using pygame or to bind with different keys. I am wondering what function I should use for one tkinter button to pause/unpause my timer if something comes up and I want to pause the timer till I'm back.
Thank you #TimRoberts, I can pause the timer now. However, I don't know how to unpause the timer to let it continue counting down.
from tkinter import *
import math
WORK_MIN = 30
BREAK_MIN = 5
reps = 0
paused = False
# --------------------------- TIMER ---------------------------- #
def start_timer():
global reps
reps += 1
work_sec = WORK_MIN * 60
break_sec = BREAK_MIN * 60
if reps % 2 == 1:
title_label.config(text="Study")
count_down(work_sec)
else:
title_label.config(text="Break")
count_down(break_sec)
window.attributes('-topmost', 0)
# ------------------------ COUNTDOWN--------------------------- #
def count_down(count):
global paused
count_min = math.floor(count / 60)
count_sec = count % 60
if count_min < 10:
count_min = f"0{count_min}"
if count_sec < 10:
count_sec = f"0{count_sec}"
canvas.itemconfig(timer_text, text=f"{count_min}:{count_sec}" )
if count > 0:
if not paused:
count -= 1
window.after(1000, count_down, count-1)
else:
start_timer()
# ---------------------------- PAUSE ------------------------------- #
def pause_function():
global paused
paused = not paused
# ---------------------------- UI ------------------------------- #
window = Tk()
title_label = Label(text="Timer")
title_label.grid(column=1, row=0)
check_marks = Label(text="")
check_marks.grid(column=1, row=4)
canvas = Canvas(width=200, height=224, bg="lightblue")
timer_text = canvas.create_text(100, 128, text="00:00", fill="white", font=("Courier", 45, "bold"))
canvas.grid(column=1, row=1)
start_button = Button(text="Start", command=start_timer)
start_button.grid(column=0, row=2)
pause_button = Button(text="Pause", command=pause_function)
pause_button.grid(column=2, row=2)
window.mainloop()
You need to do the "after" call even if you're paused, otherwise you'll never notice when you unpause. Also, since you're decrementing count once, you don't need to do it again:
def count_down(count):
count_min = count // 60
count_sec = count % 60
canvas.itemconfig(timer_text, text=f"{count_min:02d}:{count_sec:02d}" )
if count:
if not paused:
count -= 1
window.after(1000, count_down, count)
else:
start_timer()
If you want to be tricky, you could use:
if count:
count -= not paused
since True is 1 and False is 0.

How to control Progress bar size PySimpleGUI

I want to create a progress bar using PySimpleGUI but I want the user to put the maximum of the progress bar.
this is my code:
import PySimpleGUI as sg
import random, time
sg.theme("LightBlue")
progress_value=input()
layout = [[sg.Text("Enter a number out of 50", font='Lucida'),
sg.InputText(key='-PROGRESS_VALUE-', font='Lucida, 20', size=(20, 40))],
[sg.ProgressBar(progress_value, orientation='h', size=(100, 20), border_width=4, key='-PROGRESS_BAR-',
bar_color=("Blue", "Yellow"))],
[sg.Button('Change Progress'), sg.Exit(),sg.Button('Stop Progress')]]
window = sg.Window("Progress Bar", layout)
while True:
event, values = window.read()
if event == 'Exit' or event == sg.WIN_CLOSED:
break
progress_value = int(values['-PROGRESS_VALUE-'])
if event == "Change Progress":
for i in range(progress_value):
event, values = window.read(1000)
if event == "Stop Progress":
window['-PROGRESS_BAR-'].update(i-1)
break
window['-PROGRESS_BAR-'].update(max=progress_value)
window['-PROGRESS_BAR-'].update(i+1)
window.close()
as you can see the maximum which is "progress_value" is given by an input (progress_value=input())
but i want it to come from the input text of the user (sg.InputText(key='-PROGRESS_VALUE-', font='Lucida, 20', size=(20, 40))) and that value will be set to progress_value
Here's one way of doing what you're after using a single event loop
When changing the max value of a ProgressBar, you must set a current value too (in the same update call).
import PySimpleGUI as sg
sg.theme("LightBlue")
progress_value = 50
layout = [[sg.Text("Enter a number out of 50", font='Lucida'),
sg.InputText(key='-PROGRESS_VALUE-', font='Lucida, 20', size=(20, 40))],
[sg.ProgressBar(progress_value, orientation='h', size=(100, 20), border_width=4, key='-PROGRESS_BAR-',
bar_color=("Blue", "Yellow"))],
[sg.Button('Change Progress'), sg.Button('Start Progress'), sg.Button('Stop Progress')]]
window = sg.Window("Progress Bar", layout)
progress_started, counter, timeout = False, 0, None
while True:
event, values = window.read(timeout=timeout)
if event == 'Exit' or event == sg.WIN_CLOSED:
break
if event == "Change Progress":
progress_value = int(values['-PROGRESS_VALUE-'])
# NOTE - must set a current count when changing the max value
window['-PROGRESS_BAR-'].update(current_count= 0, max=progress_value)
elif event == 'Start Progress':
progress_started = True
counter = 0
timeout = 1000
elif event == 'Stop Progress':
progress_started = False
timeout = None
if progress_started:
window['-PROGRESS_BAR-'].update(counter)
counter += 1
if counter > progress_value:
progress_started = False
window.close()

Hotkeys in PySimpleGUI

I'd like to write a GUI with PySimpleGUI that can be used entirely by keyboard. Based on the following sample code:
import PySimpleGUI as sg
layout = [[sg.Text("Hello from PySimpleGUI")], [sg.Button(button_text="OK")]]
window = sg.Window("Demo", layout)
while True:
event, values = window.read()
if event == "OK" or event == sg.WIN_CLOSED:
break
window.close()
How can I add a hotkey which I can press using Alt+O to press the OK-Button? The O on the OK-Button should be underlined:
A minimalist working example derived from: https://github.com/PySimpleGUI/PySimpleGUI/issues/4122
import PySimpleGUI as sg
layout = [
[sg.Button("ok", size=(10, 2), key='button1'),
sg.Button("exit", size=(10, 2), key='button2')],
]
window = sg.Window('Hotkeys', layout, use_default_focus=False, finalize=True)
button1, button2 = window['button1'], window['button2']
window.bind("<Alt_L><o>", "ALT-o")
window.bind("<Alt_L><x>", "ALT-x")
button1.Widget.configure(underline=0, takefocus=0)
button2.Widget.configure(underline=1, takefocus=0)
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event in ("button1", "ALT-o"):
print('OK')
elif event in ("button2", "ALT-x"):
break
window.close()

How do I utilize multiprocessing module in Python?

I'm trying to add an alarm sound to my timer program, however whenever the function for the sound gets called, the program times out, and you have to wait until the sound file finishes. I've tried using asyncio and right now I'm trying to use multiprocessing modules to resolve this issue, however I've kept on hitting my head against a brick wall.
Here is my code right now for the program
import PySimpleGUI as sg
import time
from pydub import AudioSegment
from pydub.playback import play
import multiprocessing
duration = 500 # milliseconds
freq = 440 # Hz
sg.theme('DarkBrown4')
song = AudioSegment.from_mp3("alarm.mp3")
quiet_song = song - 40
def sound():
play(quiet_song)
def main():
centered_column = [[sg.T(('Set Time to Study'), font=('Consolas', 20))],
[sg.T((''), font=('Consolas', 20), key='countdown')],
[sg.B('Exit'),
sg.B('Start/Stop')], # Buttons For Timer
[sg.B('Pomodoro'),
sg.B('Short Break'),
sg.B('Long Break')
]]
layout = [[sg.Text(key='-EXPAND-', font='ANY 1', pad=(0, 0))], # the thing that expands from top
[sg.Text('', pad=(0, 0), key='-EXPAND2-'), # the thing that expands from left
sg.Column(centered_column, vertical_alignment='center', justification='center', k='-C-')]]
window = sg.Window('StudyWare Timer', layout, size=(400, 200), grab_anywhere=True, resizable=True, finalize=True)
window['-C-'].expand(True, True, True)
window['-EXPAND-'].expand(True, True, True)
window['-EXPAND2-'].expand(True, False, True)
active = False
value = 0
while True:
event, values = window.Read(timeout=1000)
# print(event, values)
if event == sg.WIN_CLOSED or event == 'Exit':
break
m, s = divmod(value, 60)
timerx = "%02d:%02d" % (m, s)
if event == 'Pomodoro':
# active = not active
value = 1
window.Element('countdown').Update(value=timerx)
if event == 'Short Break':
# active = not active
value = 300
window.Element('countdown').Update(value=timerx)
if event == 'Long Break':
# active = not active
value = 900
window.Element('countdown').Update(value=timerx)
if event == 'Start/Stop':
active = not active
if event == '__TIMEOUT__' and active:
window.Element('countdown').Update(value=timerx)
value -= 1
if value == -1:
active = not active
window.refresh()
if __name__ == '__main__':
p2 = multiprocessing.Process(target=main())
p = multiprocessing.Process(target=sound())
p2.start()
p.start()
p2.join()
p.join()
Any suggestions would be greatly appreciated! )))

comment a line of code if checkbox checked Pysimplegui

how can i comment/uncomment a line of code by checking and unchecking a checkbox in python with PySimpleGUI?
also i don't know if i wrote the code in correct way but i'm just trying to comment a line of code by checking the checkbox
any other way to do it is also fix my problem
This is my code
layout = [[sg.Text('Choose Options'))],
[sg.Checkbox('Save Posts',key="save-ed")],
[sg.Submit('Next')) ,sg.Cancel("Cancel"))] ]
window = sg.Window('my bot', layout, icon="logo.ico")
event, values = window.read()
window.close()
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == "Cancel":
break
elif values['save-ed'] == True:
save_input = ['usersave']
elif values['save-ed'] == False:
save_input = ['#usersave']
and this is the code which i want to comment or uncomment with checkbox
try:
save_input = webdriver.find_element_by_xpath('/html/body/div[4]/div[2]/div/article/div[3]/section[1]/span[4]/div/div/button/div')
save_input.click()
sleep(randint(4,5))
except NoSuchElementException:
pass
Following code show how to stop a thread to update time by a checkbox.
from datetime import datetime
from time import sleep
import threading
import PySimpleGUI as sg
def clock(window):
now = None
while timer:
new = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if new != now:
now = new
if flag:
window.write_event_value('CLOCK', now)
sleep(0.1)
sg.theme("DarkBlue3")
sg.set_options(font=("Courier New", 16))
layout = [
[sg.Text("", size=(0, 1), key='TIME')],
[sg.Checkbox("Time ON", default=True, enable_events=True, key='TIME ON')],
]
window = sg.Window('Title', layout, finalize=True)
timer, flag = True, True
threading.Thread(target=clock, args=(window,), daemon=True).start()
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
timer = False
break
elif event == 'TIME ON':
flag = values[event]
elif event == 'CLOCK':
window['TIME'].update(values[event])
window.close()
according to previous comment and answer by #jason-yang
i got the point and changed the code like this and fixed my problem
layout = [[sg.Text('Choose Options'))],
[sg.Checkbox('Save Posts',key="save-ed")],
[sg.Submit('Next')) ,sg.Cancel("Cancel"))] ]
window = sg.Window('my bot', layout, icon="logo.ico")
event, values = window.read()
window.close()
while True:
event, values = window.read()
if event == sg.WIN_CLOSED or event == "Cancel":
break
if event == "save posts":
if values['save-ed'] == True:
save_input = values['usersave']
elif values['save-ed'] == False:
save_input = values['#usersave']
the rest of the code seems to be correct and i leave it unchanged
try:
save_input = webdriver.find_element_by_xpath('/html/body/div[4]/div[2]/div/article/div[3]/section[1]/span[4]/div/div/button/div')
save_input.click()
sleep(randint(4,5))
except NoSuchElementException:
pass

Categories