Raspberry Pi and Python (TKinter and Omxplayer) - python

we made an alarm clock program in python that uses TKinter to display the word "ALARM" when the current time equals the alarm time set by the user. Now we're trying to add an alarm sound to the program, so a sound will play when the current time reaches the alarm time set.
For some weird reason when the current time reaches the alarm time the alarm sound is opened and played about 7 times all at once and then exits.
We've tried a few different things to get it to just play the sound file once but nothing has worked. Do you guys have any suggestions at all? I can post more specific information/screenshots if need be. :)
Code so far:
import time
import subprocess
#GUI Setup
from Tkinter import *
alarm_window = Tk()
alarm_window.configure(bg = 'lightblue')
alarm_window.title('Alarm Clock!')
display = Label(alarm_window, font = ('Arial', 25), bg = 'lightblue', width = 13, height = 1, borderwidth = 2)
display.grid(row = 1, column = 1, columnspan = 6)
current_time = time.strftime("%H:%M:%S")
def refresh_time():
global current_time
current_time = time.strftime("%H:%M:%S")
if current_time == times.get():
alarm_window.configure(bg='red')
timelabel.configure(bg='red')
alarmdisplay = Label(alarm_window, font = ('Arial', 25), text = 'ALARM', bg='red', width = 13, height = 1, borderwidth = 2,)
alarmdisplay.grid(row = 2, column = 1, columnspan = 6)
playProcess = subprocess.Popen(['omxplayer', '/home/pi/Digital Alarm.mp3?dl=0'])
display.config(text=current_time)
display.after(10, refresh_time)
timelabel = Label(alarm_window, text = 'Enter H:M:S', bg = 'lightblue')
timelabel.grid(row = 2, column = 1)
times = Entry(alarm_window, width = 10, bg = 'lightgrey', justify = CENTER)
times.grid(row = 2, column = 2, columnspan = 5)
times.focus()
refresh_time()
alarm_window.mainloop()

Don't know omxplayer, but if it quits after playing the sound, you could simply wait for it to finish like:
playProcess = subprocess.Popen(['omxplayer', '/home/pi/Digital Alarm.mp3?dl=0'])
playProcess.wait()

Related

Text in button not increasing

I'm creating a memory quiz app for people with dementia where it asks them about their day and they have to answer the question. A family/friend has to first fill in their memories which gets entered into a database.
I'm trying to increase the size of the text in the buttons, however the size of the button increases instead. I'm unsure on how to fix this. The answers are saved to an SQL database and retrieved from there. I am using a Guizero GUI.
Here's my code:
box1=Box(Todays_Quiz, layout = "auto", width = 1000, height = 700)
emptyspace =Text(box1, text="")
question1= Text(box1, "What time did you wake up?", width = "fill")
question1.bg= "#A1A9A9"
question1.text_size= 20
emptyspace =Text(box1, text="""
""")
wakeupAnswer1 = get_q1_answers(0)
wakeupAnswer2 = get_q1_answers(1)
wakeupAnswer3 = get_q1_answers(2)
wakeup1 = PushButton(box1, width = 50, height = 5, text = wakeupAnswer1, command=change_score1) # correct answer
emptyspace =Text(box1, text="")
wakeup2 = PushButton(box1, width = 50, height = 5, text = wakeupAnswer2, command=next_question1)
emptyspace =Text(box1, text="")
wakeup3 = PushButton(box1, width = 50, height = 5, text = wakeupAnswer3, command=next_question1)
wakeup1.bg="#fa7a7a"
wakeup1.text_size = 15
wakeup2.bg="#fa7a7a"
wakeup2.text_size =15
wakeup3.bg="#fa7a7a"
wakeup3.text_size = 15

Python Tkinter: object has no attribute tk

I am new to tkinter, python, and programming in general. I have made an example program of what I'm trying to do. I am trying to use tkinter GUI to receive user inputs for date and time, then convert these tk entries into strings, then check the format of the date and time strings, then if the format is good add the date and time to a list. My issue is with converting the tk entries into strings. When I try to do so I receive an error that says "Example object has no attribute tk". In my program, I have a tk window that is made in my UserInputWindow function, and I pass this window to PromptDateTime, which is where the user is prompted to enter a date and time. When I try to convert using "dateFromUser = tk.Entry(self)", this is the part that receives the error. I don't understand why the PromptDateTime function had no problem editing the window from UserInputWindow function, yet when tk is directly referenced there is an issue.
Also: I had some trouble with formatting my code below (new to stack overflow) so please note that the first section of code is part of "class Example()", and the second section of code is the main function.
Thank you for your help! Please be nice! I'm a newbie and open to critiques.
class Example():
#data members
__dateEntry = None
__timeEntry = None
exampleList = []
def UserInputWindow(self, windowName, instruction):
#Create new window to display fields and options
new_window = tk.Tk()
new_window.title(f'{windowName}')
new_window.geometry = ("500x500")
#Label to display instructions
label_instruction = Label(new_window, text = (f'{instruction}'), font = ("Courier", 10), justify = LEFT, fg = "black", bg = "light yellow")
label_instruction.grid(row = 0, column = 0)
return new_window
#this function checks to see if date string from user is in proper format, and if it is not an error window appears.
def VerifyDate(self, d):
#code deleted for simplicty for this example
#this function checks to see if time string from user is in proper format, and if it is not an error window appears.
def VerifyTime(self, t):
#code deleted for simplicty for this example
#this function prompts user for date and time
def PromptDateTime(self, new_window):
#Label to display instructions
label_instruction = Label(new_window, text = "Enter activity date and time: ",font = ("Courier", 10), justify = LEFT, fg = "black", bg = "light yellow")
label_instruction.grid(row = 0, column = 0)
#Create labels and entries for date and time
label_date = Label(new_window, text = "Enter date in MM/DD/YYYY format: ",fg = "black", bg = "white")
label_date.grid(row = 1, column = 0, padx = 5)
dateEntry = Entry(new_window, fg = 'black', bg = 'white', width = 10)
dateEntry.grid(row = 2, column = 0, padx = 5)
dateFromUser = tk.Entry(self)
str(dateFromUser)
label_time = Label(new_window, text = "Enter time in hh:mm format (military time): ",fg = "black", bg = "white")
label_time.grid(row = 3, column = 0, padx = 5)
timeEntry = Entry(new_window, fg = 'black', bg = 'white', width = 10)
timeEntry.grid(row = 4, column = 0, padx = 5)
self.VerifyDate(dateFromUser)
self.VerifyTime(timeEntry)
def SubmitButton(self, new_window, new_command):
button_submit = Button(new_window, fg = "black", bg = "light blue", text = "Submit", command = new_command)
button_submit.grid(row = 17, column = 10, pady = 5)
def PromptAndAddToList(self):
window = self.UserInputWindow('Date and Time', 'Enter date and time as specified below.')
self.PromptDateTime(window)
self.SubmitButton(window, lambda:exampleList.append(otherClass(dateEntry, timeEntry)))
#################################################
if __name__ == '__main__':
from tkinter import *
import tkinter as tk
import datetime
ex = Example()
ex.PromptAndAddToList()
root = tk.Tk()
root.withdraw()
root.mainloop()
As the error said, the parent of dateFromUser is Example:
dateFromUser = tk.Entry(self) # self is instance of class Example
but Example is not a tkinter widget.
Use new_window instead of self:
dateFromUser = tk.Entry(new_window)

Python Tkinter using Raspberry Pi updating GUI

recently i have been working on Tkinter in Raspberry pi to build a GUI for home automation, and i wanted to design a validation module which says "ACTIVE" when the sensor is working and "INACTIVE" when sensor has malfunctioned.
I am able to get the validation in the GUI but its not dynamic. Every time i have to re-run the program to get the updated status of the sensors
is there a way where in i can update the Active and Inactive status without re-running the entire program?
I am taking input from GPIO pins on the raspberry pi and reading them in the program
here is the code i have worked on so far:
import RPi.GPIO as GPIO
import time
from tkinter import *
from tkinter import ttk
import tkinter.font
GPIO.setwarnings(False)
Sig1 = 7
GPIO.setmode(GPIO.BOARD)
GPIO.setup(Sig1, GPIO.IN)
out1 = 11# pin11
GPIO.setmode(GPIO.BOARD) # We are accessing GPIOs according to their physical location
GPIO.setup(out1, GPIO.OUT) # We have set our LED pin mode to output
GPIO.output(out1, GPIO.LOW)
gui = Tk()
gui.title("tkinter")
gui.config(background = "gray86")
gui.minsize(1050,620)
Font1 = tkinter.font.Font(family = 'Courier 10 Pitch', size = 20, weight = 'bold')
Font2 = tkinter.font.Font(family = 'Courier 10 Pitch', size = 18, weight = 'bold')
Font3 = tkinter.font.Font(family = 'Courier 10 Pitch', size = 9, weight = 'bold')
def func1_on():
GPIO.output(out1, GPIO.HIGH) # led on
Text1 = Label(gui,text=' ON ', font = Font2, bg = 'gray84', fg='green3', padx = 0)
Text1.grid(row=8,column=1)
def func1_off():
GPIO.output(out1, GPIO.LOW) # led off
Text2 = Label(gui,text='OFF', font = Font2, bg = 'gray84', fg='red', padx = 0)
Text2.grid(row=8,column=1)
label_2 = Label(gui,text='Sensor:', font = Font2, fg='gray40', bg = 'gray84', padx = 10, pady = 10)
label_2.grid(row=6,column=0)
if GPIO.input(Sig1) == True:
Text3 = Label(gui,textvariable=' Active ',relief = "raised", font = Font2, bg = 'gray84', fg='green3', padx = 0)
Text3.grid(row=6,column=1)
else:
Text4 = Label(gui,textvariable=' Inactive ',relief = "raised", font = Font2, bg = 'gray84', fg='red', padx = 0)
Text4.grid(row=6,column=1)
Button1 = Button(gui, text='Switch On', font = Font3, command = func1_on, bg='gray74', height = 1, width = 7)
Button1.grid(row=8,column=0)
Button2 = Button(gui, text='Switch Off', font = Font3, command = func1_off, bg='gray74', height = 1, width = 7)
Button2.grid(row=9,column=0)
gui.mainloop()
it would be greatly appreciated if someone could help me with this.
Use root.after(milliseconds, function_name) run some function periodically.
This function should check sensors, update text in labels and use root.after to run again after some time.
BTW: function_name should be without ()
Minimal example. It updates label with current time
import tkinter as tk
import time
# --- functions ---
def check():
# update text in existing labels
label['text'] = time.strftime('%H:%M:%S')
# run again after 1000ms (1s)
root.after(1000, check)
# --- main ---
root = tk.Tk()
label = tk.Label(root)
label.pack()
check() # run first time
root.mainloop()

Disable submit button until all fields are filled in Python Tkinter

I'm currently working on a project and when my program launches it needs to get the names off of two players before beginning, the way my program is at the moment, it is possible to press submit without entering names, how do I prevent this?
Many Thanks Jayode18
# Program by Jack O'Donnell (Jayode18 StackOverflow/GitHub)
# Date Started: 16th March 2019
# Import winsound and create functions for each of the sound effects & their functionalities.
import tkinter
import time
from tkinter import *
import random
import winsound
def gameOver():
winsound.PlaySound("Gameover", winsound.SND_FILENAME)
def pointsDrop():
winsound.PlaySound("Points drop", winsound.SND_FILENAME)
def pointsGain():
winsound.PlaySound("Points gain", winsound.SND_FILENAME)
def flipCoin():
winsound.PlaySound("coinflip", winsound.SND_FILENAME)
def rollDice():
winsound.PlaySound("Dice", winsound.SND_FILENAME)
# Other Definitions #
def goToPrimary():
primaryWindow = tkinter.Tk()
primaryWindow.iconbitmap("icon.ico")
primaryWindow.geometry("500x500")
primaryWindow.title("YGO Calculator ver. 1.0 ALPHA")
gainLPButton = Button(primaryWindow, text = "LP Gain", command = pointsGain)
gainLPButton.grid(row = 2, column = 1)
loseLPButton = Button(primaryWindow, text = "LP Loss", command = pointsDrop)
loseLPButton.grid(row = 2, column = 2)
gameOverButton = Button(primaryWindow, text = "LP = 0", command = gameOver)
gameOverButton.grid(row = 2, column = 3)
flipCoinButton = Button(primaryWindow, text = "Coin Toss", command = flipCoin)
flipCoinButton.grid(row = 2, column = 4)
rollDiceButton = Button(primaryWindow, text = "Roll Dice", command = rollDice)
rollDiceButton.grid(row = 2, column = 5)
# Button Commands #
def OnSubmit():
e = entry_duelist.get()
print(e)
time.sleep(0.25)
window.destroy()
goToPrimary()
# import tkinter and create the window window, then populate it with buttons to text window.
window = tkinter.Tk()
window.resizable(width = False, height = False)
window.title("YGO Calculator ver. 1.0 ALPHA")
window.iconbitmap('icon.ico') # Give window the correct icon
mainWindow = Frame(window)
window.geometry("180x75")
label_duelist = Label(mainWindow, text="Duelist 1:")
label_duelist_2 = Label(mainWindow, text="Duelist 2:")
entry_duelist = Entry(mainWindow)
entry_duelist_2 = Entry(mainWindow)
label_duelist.grid(row = 4, column = 0)
label_duelist_2.grid(row = 5, column = 0)
entry_duelist.grid(row = 4, column = 1, columnspan = 4)
entry_duelist_2.grid(row = 5, column = 1, columnspan = 4)
submit_button = Button(mainWindow, text = "Submit", command = OnSubmit)
submit_button.grid(row = 7, column = 4, columnspan = 2)
mainWindow.grid(row = 5, column = 0)
window.mainloop()
All help would be amazing, thanks again! :D
Thank you everyone for your help. I managed to find a working solution!
You can check the entry texts for example
if entry1.get() != "" And entry2.get() != "":
# entries are not empty, do something

My Matplotlib graph displays as expected but stops the script until I close it, then the script continues

I wrote a script for the Collatz Conjecture within a Tkinter GUI and displaying a Matplotlib graph of the result of the algorithm. Everything works OK except, the graph displays perfectly but then stops the script without populating the data field which displays the data points on which the graph is based. Only when I close the graph does is the data field populated.
I'm sure I'm doing something wrong in the script (probably indents) but I've tried everything but can't make it work. The script is added below:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from tkinter import *
from tkinter import messagebox
root=Tk()
root.title("Collatz Conjecture")
import matplotlib.pyplot as plt
import textwrap
from IPython import get_ipython
# sget_ipython().run_line_magic('matplotlib', 'qt')
#import matplotlib.backends.tkagg as tkagg
#from matplotlib.backends.backend_agg import FigureCanvasAgg
# Functions
lst = []
def collatz(num):
lst.clear()
while num != 1:
lst.append(num)
if num % 2 == 0:
num = int(num / 2)
else:
num = int(3 * num + 1)
def main(event):
num = int(input.get())
collatz(num)
plt.plot(lst)
plt.show()
output1.delete(1.0, END)
output1.insert(END, lst)
output2.delete(1.0, END)
output2.insert(END, "Number of iterations: " + str(len(lst)))
lbl1 = Label(root, width = 20, text = "Type in number\n & press Enter")
lbl1.grid(row = 1, column = 0, sticky = W)
lbl2 = Label(root, width = 40, text = "THE COLLATZ CONJECTURE")
lbl2.grid(row = 4, column = 0)
input = Entry(root, width = 20, bg = "light grey")
input.grid(row = 1, padx = 6, sticky = E)
input.get()
input.bind("<Return>", main)
canv = Canvas(root, width= 350, height= 350, bg = "white")
canv.grid(row = 6, column = 0, padx = (5,5), pady = (5,5))
img = PhotoImage(file = "/Users/andrehuman/Desktop/collatz_conjecture.png") # Tip to get full path: pull file into terminal!
canv.create_image(25, 25, image = img, anchor = NW)
bt1 = Button(root, width = 10, text = "About")
bt1.grid(row = 7, column = 0, pady = (5,7))
output1 = Text(root, wrap = WORD, width = 50, height = 7, bg = "light grey") # Note word wrap attribute
output1.grid(row = 3, column = 0, padx = (5,1), sticky = W)
output2 = Text(root, width = 50, height = 1, bg = "white")
output2.grid(row = 2, column = 0, sticky = W)
def about():
messagebox.showinfo("About", "The Collatz conjecture states that if you pick any positive whole number, and if its even, you divide it by two and if its odd, you multiply it by three and add one, and if you repeat this procedure often enough, the number that you started with will eventually reduce to one and if you play this game for long enough, your friends will eventually stop calling to see if you want to hang out ")
btn1 = Button(root, text = "About", command = about)
btn1.grid(row = 7, column = 0, pady = (5,7))
root.mainloop()
Any help would be appreciated.
Andre
PS, I'm not a professional coder or student coder as such... I'm doing this as a hobby and to inform myself about coding.

Categories