Please note: this is a self answered question for reference.
Using Python how does one display a notification:
a) with a timeout and
b) have the timeout visibly countdown.
Update: Just 2 weeks after posting this, I updated to Mint 19 (Ubuntu 18.04) the result, the timer function described below has vanished in the Standard Notification. I can only suppose that the move to GTK+3 has virtually hidden the timer. It is there but barely visible.
Choosing Notification style Nodoka or Coco in Control Centre --> Popup Notifications, does display the timer properly.
End Update
Using the standard notification modules notify2 and Notify in the gi.repository, one simply has to add an action, even though you have no intention of using it.
Note: there appears to be no documented reason for this, that I have found.
In addition to a close button being added to the notification, it also supplies a clockface that reduces based on the timeout supplied.
for notify2:
import notify2
class Notify():
def mess_callback():
pass
def __init__(self,parent,caption,msg,timeout=None,urgency=None):
if timeout != None: pass
else: timeout = 0 # message should not timeout
if urgency: pass
else: urgency = 0
img = '/home/rolf/MyApp.png'
caps = notify2.get_server_caps()
mess = notify2.Notification(caption,msg,img) # passing an image is optional
mess.set_timeout(timeout) #milliseconds
mess.set_urgency(urgency) #0-Low, 1-Normal, 2-Critical
# Without the following `add_action` option, No countdown to the time out is shown
if timeout != 0 and 'actions' in caps:
mess.add_action("close","Close",self.mess_callback,None) #Show the countdown to close
mess.show()
if __name__ == "__main__":
notify2.init("MyApp") #Register MyApp
Notify(None,"Error","This message is not timed and has to be manually cancelled")
Notify(None,"Error 2","This message will timeout after the default value",timeout=-1)
Notify(None,"Information","An Unimportant message",timeout=20000,urgency=0)
Notify(None,"Attention","An Important message",timeout=20000,urgency=1)
Notify(None,"Emergency","A Critical message",timeout=20000,urgency=2)
notify2.uninit() #Un-register
Using Notify in the gi.repository:
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
class Message():
def mess_callback():
pass
def __init__(self,parent,caption,msg,timeout=None,urgency=None):
if timeout != None: pass
else: timeout = 0 # message should not timeout
if urgency: pass
else: urgency = 0
img = '/home/rolf/MyApp.png'
caps = Notify.get_server_caps()
mess = Notify.Notification.new(caption, msg, img) # passing an image is optional
mess.set_timeout(timeout) #milliseconds
mess.set_urgency(urgency) #0-Low, 1-Normal, 2-Critical
# Without the following `add_action` option, No countdown to the time out is shown
if timeout != 0 and 'actions' in caps:
mess.add_action("close","Close",self.mess_callback,None) #Show the countdown to close
mess.show()
if __name__ == "__main__":
Notify.init("MyApp") #Register MyApp
Message(None,"Error","This message is not timed and has to be manually cancelled")
Message(None,"Error 2","This message will timeout after the default value",timeout=-1)
Message(None,"Information","An Unimportant message",timeout=20000,urgency=0)
Message(None,"Attention","An Important message",timeout=20000,urgency=1)
Message(None,"Emergency","A Critical message",timeout=20000,urgency=2)
Related
scenes = obs.obs_frontend_get_scenes()
def script_load(settings):
obs.obs_frontend_add_event_callback(onevent)
def script_update(settings):
global trigger, s_minutes, s_seconds, ending, e_minutes, e_seconds
trigger = obs.obs_data_get_string(settings, "e_trigger scene")
s_minutes = obs.obs_data_get_int(settings, "s_minutes")
s_seconds = obs.obs_data_get_int(settings, "s_seconds")
e_minutes = obs.obs_data_get_int(settings, "e_minutes")
e_seconds = obs.obs_data_get_int(settings, "e_seconds")
ending = obs.obs_data_get_string(settings, "s_ending scene")
def timer_callback():
global tElapsed
if state == 0:
print("Error: State = 0")
obs.remove_current_callback()
if state == 1:
tElapsed += 1
print(tElapsed)
if tElapsed == timer:
tElapsed = 0
set_scene()
obs.remove_current_callback()
if state == 2:
tElapsed += 1
print(tElapsed)
if tElapsed == timer:
tElapsed = 0
obs.obs_frontend_streaming_stop()
obs.remove_current_callback()
def set_scene():
index = (obs.obs_frontend_get_scene_names()).index(ending)
scene = scenes[index]
obs.obs_frontend_set_current_scene(scene)
def onevent(event):
global state, timer
if event==obs.OBS_FRONTEND_EVENT_STREAMING_STOPPED:
state = 0
if event==obs.OBS_FRONTEND_EVENT_STREAMING_STARTED:
state = 1
timer = s_minutes * 60 + s_seconds
obs.timer_add(timer_callback,1000)
if event==obs.OBS_FRONTEND_EVENT_SCENE_CHANGED:
if obs.obs_source_get_name(obs.obs_frontend_get_current_scene()) == trigger:
state = 2
timer = e_minutes * 60 + e_seconds
obs.timer_add(timer_callback,1000)
else:
obs.timer_remove(timer_callback)
if state == 1:
print("Start timer stopped")
elif state == 2:
print("End timer stopped")
When I try to set the scene from within a timer callback function, OBS ends up crashing. I've tried to print the number for every time the callback function is called, and when I look at the logs, it shows every print function it's supposed to call, but it doesn't tell me why OBS crashed.
This is the code where I use a helper function to set the scene. With or without the helper function, it crashes either way. However, when I set the scene from outside the timer, everything works fine.
Any form of help is appreciated!
It's been a while since I worked on this script, but I have managed to fix the problem so I'll try my best to recall how I fixed it.
Resolution
It appears that the crash only occurs when the combination of the 3 functions (obs_frontend_add_event_callback(), timer_add() and obs_frontend_set_current_scene()) appears, so instead of using the frontend event callback, I used signal handlers instead.
I first got the current scene and got the signal handler of that scene. Afterwards I connected a callback function which runs when the signal handler sends a "deactivate" signal. From there I added the timer callback which switches the scene when the timer reaches 0. This stopped the crashes from happening. My code for reference:
current_scene = obs.obs_frontend_get_current_scene()
current_handler = obs.obs_source_get_signal_handler(current_scene)
obs.obs_source_release(current_scene)
obs.signal_handler_connect(current_handler, "deactivate", checker)
timer = s_minutes * 60 + s_seconds
obs.timer_add(timer_callback, 1000)
Notes:
For some reason, even when obs_frontend_add_event_callback() is being used in another script, using obs_frontend_set_current_scene() within timer_add() continues to cause the crash. I have concluded that obs_frontend_add_event_callback() should be avoided.
I switched to attaching a stream signal handler in script_load() to signal when the stream started. However, the signal handler only calls the callback function from the second stream onwards of the OBS instance. We had to use the frontend event callback at this point. So my steps was to add the frontend event callback on script load with the callback function removing the frontend event callback immediately and replacing it with a signal handler instead for future streams on the same instance.
def script_load(settings):
obs.obs_frontend_add_event_callback(onevent)
def onevent(event):
global output
if event == obs.OBS_FRONTEND_EVENT_STREAMING_STARTED:
stream = obs.obs_frontend_get_streaming_output()
output = obs.obs_output_get_signal_handler(stream)
obs.signal_handler_connect_global(output, frontevent)
obs.obs_output_release(stream)
obs.obs_frontend_remove_event_callback(onevent)
Another problem I faced with signal handlers: when toggling studio mode, the signal handler of the current source will emit a "deactivate" signal, which I am unsure of how to fix. Suggestions of course are welcome.
References:
Signal handlers: https://obsproject.com/docs/reference-libobs-callback.html
Frontend API: https://obsproject.com/docs/reference-frontend-api.html
Outputs: https://obsproject.com/docs/reference-outputs.html
Sources: https://obsproject.com/docs/reference-sources.html
Scripting: https://obsproject.com/docs/scripting.html
This might be a bit late, and I'm relatively new to programming to OBS, but I noticed that your code uses "obs_frontend_get_current_scene()". There are some calls, including this one, that create pointers whenever they are called. These pointers have to be released or they continue consuming more and more resources.
According to the API documentation (https://obsproject.com/docs/reference-frontend-api.html?highlight=obs_frontend_get_current_scene), a new reference is created when this is called. Although it isn't obvious from this documentation entry, the reference can be released with "obs.obs_source_release(<sourcename>)", because obs_frontend_get_current_scene() returns a source.
I don't know if a reference is created when used inline (without assigning it to a variable) as you have done, but this might be the problem. Try assigning obs_frontend_get_current_scene() to a variable, using the variable in the obs_source_get_name(<source name>) call, and then 'release' the variable. Something like this:
scene_as_source = obs.obs_frontend_get_current_scene()
if obs.obs_source_get_name(scene_as_source) == trigger:
state = 2
timer = e_minutes * 60 + e_seconds
obs.timer_add(timer_callback,1000)
else:
obs.timer_remove(timer_callback)
if state == 1:
print("Start timer stopped")
elif state == 2:
print("End timer stopped")
obs.obs_source_release(scene_as_source)
I wanted to add a filter to the python logger in my Django project; the filter has a while loop in it (there to wait and allow the user to provide a response). However, when I try to implement my filter the server seems to immediately break and my view goes into an infinite loop (which doesn't happen when I test the filter in a terminal window).
My code I have looks like this:
import logging, time, os
# in my Utils.py file:
def ErrorReport(msg, notify=False, warnOnly=False):
logger.exception("ErrorReport:%s" % msg, exc_info=True)
raise RuntimeError('Aborting execution. Terrible things might happen. Check logs for more information')
# in my views.py file:
class ErrorFilter(logging.Filter):
def __init__(self,level):
self.level = level
print level
def filter(self,record):
if record.levelno == self.level:
os.environ["ERROR_FLAG"] = "True"
timeout = time.time() + 60*1 #set the timeout to 1 minute
while True:
print "waiting..."
keep_going = os.environ.get("CONTINUE_FLAG")
#wait for user to respond
if keep_going == "False" or time.time() > timeout:
Utils.ErrorReport("There's a problem, quiting current process.")
if keep_going == "True":
break
logger = Utils.getLogger() #I format the logger in another python script called Utils
logger.addFilter(ErrorFilter(logging.ERROR))
def home(request):
context_dict = {'boldmessage': "--> To view logs, please turn off any popup blockers <--",'version':"1.1.6"}
logger.error("faking an error to test new filter")
return render(request, 'InterfaceApp/home.html', context_dict)
I've tested a simpler version of this filter in a terminal window and didn't have any problems with it getting stuck in an infinite loop. When I try to open the home page of my django site it looks like it's loading forever, and when I look at the developers window I can see that I've lost the connection to my server. In the terminal window where I'm running the server, I get the print statement waiting... forever, even though I've set and tested the timeout on the while loop.
Is there a fundamental problem with running while loops in a django project, or have I missed something?
I build a keypad activated sensor with a Raspberry Pi and Python. Everything seems to go well but after a few minutes and several keypad entries, the Pi has a total crash and switches off immediately - no error messages pop up.
The script will continously wait for a keypad entry and if the code is correct, switch on a sensor, if it's not you have to try again. If the sensor is activated you have to put in the correct numbers to avoid an alarm being triggered after 30s)
Could anyone point me in the direction of what might be the problem? Here are the things I've tried so far without success
1) Exchange Pi to new Pi 2
2) Different OS, both NOOBS and Raspbian Wheezy
3) Different sensor (accelerometer vs IR sensor)
4) Disconnect monitor, keyboard and use SSH connection via Cygwin
5) Get log file until crash - log file was empty after reboot
python bad_script &> full_log.txt
6) Different log file command: causes instant crash and is also empty after reboot:
python script.py >> /logdir/script.py.log 2>&1
The question is: how am I able to crash Linux? If it's a memory problem, isn't there a prevention in linux to stop processed before they appear?
Here is the full script I am running:
import sys
from time import sleep
import threading
import signal
from matrix_keypad import RPi_GPIO1
import RPi.GPIO as GPIO
import smbus
import time
passcode = [1,2,3,4] # this is the correct code you have to enter
kp = RPi_GPIO1.keypad(columnCount = 3)
alarm_active = threading.Event() # shared variable used to control the sensor monitoring thread
alarm_active.clear() # Clear the alarm initially
monitor_thread = None # Global variable used to store the monitoring thread
#Set up all the pins correctio
GPIO.setmode(GPIO.BCM)
PINIR=7
GPIO.setup(7, GPIO.IN) # infrad-sensor
#Now activate the kaypad and listen for keypad inputs
def digit():
r = None
while r == None:
r = kp.getKey()
return r
def get_keycode():
# Function to loop around reading 4 keypresses in a row
# Compare against chosen code
# If match, switch the alarm state
entered = []
while len(entered) < 4:
key = digit()
sleep(0.5)
print key
entered.append( key )
if entered == passcode:
entered = []
print "Code correct"
switch_alarm_state()
else:
# Just clear the keypad buffer if the wrong code went in
# Could say "BAD CODE!" here, or even force immediate alarm perhaps
print "Wrong Code - Try again"
GPIO.output(27, True) # let red LED blink as indicator that code was wrong
time.sleep(1)
GPIO.output(27, False)
entered = []
def switch_alarm_state():
# Function to control the state of the alarm
# If the alarm should be on, run a thread monitoring the sensor
# If the alarm should be off, make sure the thread is stopped
global monitor_thread
if alarm_active.is_set():
# If the alarm is currently set, stop it
print "Alarm was abolished"
GPIO.output(17, False) #switch green LED off
alarm_active.clear() # Signals to the monitor thread that we wish to shut it down
monitor_thread.join() # Wait until the monitor thread has stopped
else:
# Alarm is not currently set, so start the sensor monitor thread going
print "Alarm was activated"
GPIO.output(17, True)
monitor_thread = threading.Thread( target=sensor_monitor )
alarm_active.set()
monitor_thread.start()
def sensor_monitor():
# Function to be run in a separate thread, monitoring the sensor
alarm_timer = None # Variable to hold a timer object that will fire the alarm
while alarm_active.is_set():
#print xrota
if GPIO.input(PINIR):
print "Alarm has been triggered"
if alarm_timer is None:
alarm_timer = threading.Timer( 30.0, fire_alarm )
alarm_timer.start()
sleep(0.5)
# The alarm must have been deactivated to get here
# Stop any outstanding alarms and shutdown
if alarm_timer is not None:
alarm_timer.cancel()
return
def fire_alarm():
# Here you implement the actions to be carried out by an alarm
print "Alarm is send to server"
msg = "Alarm"
publish.single("alarm/demo",msg, hostname="52.17.194.125")
def shutdown_handler( signum, frame ):
# Shut down the child thread nicely
alarm_active.clear()
monitor_thread.join()
if __name__ == '__main__': # This is the Python way to check you are being directly run, and not just imported by another script
signal.signal( signal.SIGINT, shutdown_handler ) # If you stop the script with ctrl+C, make sure it shuts down properly
signal.signal( signal.SIGTERM, shutdown_handler )
try:
while True:
get_keycode()
except KeyboardInterrupt:
GPIO.cleanup()
print "Quit program"
The next script I'm using is used to listen to IMAP connection using IMAP IDLE and depends heavily on threads. What's the easiest way for me to eliminate the treads call and just use the main thread?
As a new python developer I tried editing def __init__(self, conn): method but just got more and more errors
A code sample would help me a lot
#!/usr/local/bin/python2.7
print "Content-type: text/html\r\n\r\n";
import socket, ssl, json, struct, re
import imaplib2, time
from threading import *
# enter gmail login details here
USER="username#gmail.com"
PASSWORD="password"
# enter device token here
deviceToken = 'my device token x x x x x'
deviceToken = deviceToken.replace(' ','').decode('hex')
currentBadgeNum = -1
def getUnseen():
(resp, data) = M.status("INBOX", '(UNSEEN)')
print data
return int(re.findall("UNSEEN (\d)*\)", data[0])[0])
def sendPushNotification(badgeNum):
global currentBadgeNum, deviceToken
if badgeNum != currentBadgeNum:
currentBadgeNum = badgeNum
thePayLoad = {
'aps': {
'alert':'Hello world!',
'sound':'',
'badge': badgeNum,
},
'test_data': { 'foo': 'bar' },
}
theCertfile = 'certif.pem'
theHost = ('gateway.push.apple.com', 2195)
data = json.dumps(thePayLoad)
theFormat = '!BH32sH%ds' % len(data)
theNotification = struct.pack(theFormat, 0, 32,
deviceToken, len(data), data)
ssl_sock = ssl.wrap_socket(socket.socket(socket.AF_INET,
socket.SOCK_STREAM), certfile=theCertfile)
ssl_sock.connect(theHost)
ssl_sock.write(theNotification)
ssl_sock.close()
print "Sent Push alert."
# This is the threading object that does all the waiting on
# the event
class Idler(object):
def __init__(self, conn):
self.thread = Thread(target=self.idle)
self.M = conn
self.event = Event()
def start(self):
self.thread.start()
def stop(self):
# This is a neat trick to make thread end. Took me a
# while to figure that one out!
self.event.set()
def join(self):
self.thread.join()
def idle(self):
# Starting an unending loop here
while True:
# This is part of the trick to make the loop stop
# when the stop() command is given
if self.event.isSet():
return
self.needsync = False
# A callback method that gets called when a new
# email arrives. Very basic, but that's good.
def callback(args):
if not self.event.isSet():
self.needsync = True
self.event.set()
# Do the actual idle call. This returns immediately,
# since it's asynchronous.
self.M.idle(callback=callback)
# This waits until the event is set. The event is
# set by the callback, when the server 'answers'
# the idle call and the callback function gets
# called.
self.event.wait()
# Because the function sets the needsync variable,
# this helps escape the loop without doing
# anything if the stop() is called. Kinda neat
# solution.
if self.needsync:
self.event.clear()
self.dosync()
# The method that gets called when a new email arrives.
# Replace it with something better.
def dosync(self):
print "Got an event!"
numUnseen = getUnseen()
sendPushNotification(numUnseen)
# Had to do this stuff in a try-finally, since some testing
# went a little wrong.....
while True:
try:
# Set the following two lines to your creds and server
M = imaplib2.IMAP4_SSL("imap.gmail.com")
M.login(USER, PASSWORD)
M.debug = 4
# We need to get out of the AUTH state, so we just select
# the INBOX.
M.select("INBOX")
numUnseen = getUnseen()
sendPushNotification(numUnseen)
typ, data = M.fetch(1, '(RFC822)')
raw_email = data[0][1]
import email
email_message = email.message_from_string(raw_email)
print email_message['Subject']
#print M.status("INBOX", '(UNSEEN)')
# Start the Idler thread
idler = Idler(M)
idler.start()
# Sleep forever, one minute at a time
while True:
time.sleep(60)
except imaplib2.IMAP4.abort:
print("Disconnected. Trying again.")
finally:
# Clean up.
#idler.stop() #Commented out to see the real error
#idler.join() #Commented out to see the real error
#M.close() #Commented out to see the real error
# This is important!
M.logout()
As far as I can tell, this code is hopelessly confused because the author used the "imaplib2" project library which forces a threading model which this code then never uses.
Only one thread is ever created, which wouldn't need to be a thread but for the choice of imaplib2. However, as the imaplib2 documentation notes:
This module presents an almost identical API as that provided by the standard python library module imaplib, the main difference being that this version allows parallel execution of commands on the IMAP4 server, and implements the IMAP4rev1 IDLE extension. (imaplib2 can be substituted for imaplib in existing clients with no changes in the code, but see the caveat below.)
Which makes it appear that you should be able to throw out much of class Idler and just use the connection M. I recommend that you look at Doug Hellman's excellent Python Module Of The Week for module imaplib prior to looking at the official documentation. You'll need to reverse engineer the code to find out its intent, but it looks to me like:
Open a connection to GMail
check for unseen messages in Inbox
count unseen messages from (2)
send a dummy message to some service at gateway.push.apple.com
Wait for notice, goto (2)
Perhaps the most interesting thing about the code is that it doesn't appear to do anything, although what sendPushNotification (step 4) does is a mystery, and the one line that uses an imaplib2 specific service:
self.M.idle(callback=callback)
uses a named argument that I don't see in the module documentation. Do you know if this code ever actually ran?
Aside from unneeded complexity, there's another reason to drop imaplib2: it exists independently on sourceforge and PyPi which one maintainer claimed two years ago "An attempt will be made to keep it up-to-date with the original". Which one do you have? Which would you install?
Don't do it
Since you are trying to remove the Thread usage solely because you didn't find how to handle the exceptions from the server, I don't recommend removing the Thread usage, because of the async nature of the library itself - the Idler handles it more smoothly than a one thread could.
Solution
You need to wrap the self.M.idle(callback=callback) with try-except and then re-raise it in the main thread. Then you handle the exception by re-running the code in the main thread to restart the connection.
You can find more details of the solution and possible reasons in this answer: https://stackoverflow.com/a/50163971/1544154
Complete solution is here: https://www.github.com/Elijas/email-notifier
I have an FTP function that traces the progress of running upload but my understanding of threading is limited and i have been unable to implement a working solution... I'd like to add a GUI progress bar to my current Application by using threading. Can someone show me a basic function using asynchronous threads that can be updated from another running thread?
def ftpUploader():
BLOCKSIZE = 57344 # size 56 kB
ftp = ftplib.FTP()
ftp.connect(host)
ftp.login(login, passwd)
ftp.voidcmd("TYPE I")
f = open(zipname, 'rb')
datasock, esize = ftp.ntransfercmd(
'STOR %s' % os.path.basename(zipname))
size = os.stat(zipname)[6]
bytes_so_far = 0
print 'started'
while 1:
buf = f.read(BLOCKSIZE)
if not buf:
break
datasock.sendall(buf)
bytes_so_far += len(buf)
print "\rSent %d of %d bytes %.1f%%\r" % (
bytes_so_far, size, 100 * bytes_so_far / size)
sys.stdout.flush()
datasock.close()
f.close()
ftp.voidresp()
ftp.quit()
print 'Complete...'
Here's a quick overview of threading, just in case :) I won't go into too much detail into the GUI stuff, other than to say that you should check out wxWidgets. Whenever you do something that takes a long time, like:
from time import sleep
for i in range(5):
sleep(10)
You'll notice that to the user, the entire block of code seems to take 50 seconds. In those 5 seconds, your application can't do anything like update the interface, and so it looks like it's frozen. To solve this problem, we use threading.
Usually there are two parts to this problem; the overall set of things you want to process, and the operation that takes a while, that we'd like to chop up. In this case, the overall set is the for loop and the operation we want chopped up is the sleep(10) function.
Here's a quick template for the threading code, based on our previous example. You should be able to work your code into this example.
from threading import Thread
from time import sleep
# Threading.
# The amount of seconds to wait before checking for an unpause condition.
# Sleeping is necessary because if we don't, we'll block the os and make the
# program look like it's frozen.
PAUSE_SLEEP = 5
# The number of iterations we want.
TOTAL_ITERATIONS = 5
class myThread(Thread):
'''
A thread used to do some stuff.
'''
def __init__(self, gui, otherStuff):
'''
Constructor. We pass in a reference to the GUI object we want
to update here, as well as any other variables we want this
thread to be aware of.
'''
# Construct the parent instance.
Thread.__init__(self)
# Store the gui, so that we can update it later.
self.gui = gui
# Store any other variables we want this thread to have access to.
self.myStuff = otherStuff
# Tracks the paused and stopped states of the thread.
self.isPaused = False
self.isStopped = False
def pause(self):
'''
Called to pause the thread.
'''
self.isPaused = True
def unpause(self):
'''
Called to unpause the thread.
'''
self.isPaused = False
def stop(self):
'''
Called to stop the thread.
'''
self.isStopped = True
def run(self):
'''
The main thread code.
'''
# The current iteration.
currentIteration = 0
# Keep going if the job is active.
while self.isStopped == False:
try:
# Check for a pause.
if self.isPaused:
# Sleep to let the os schedule other tasks.
sleep(PAUSE_SLEEP)
# Continue with the loop.
continue
# Check to see if we're still processing the set of
# things we want to do.
if currentIteration < TOTAL_ITERATIONS:
# Do the individual thing we want to do.
sleep(10)
# Update the count.
currentIteration += 1
# Update the gui.
self.gui.update(currentIteration,TOTAL_ITERATIONS)
else:
# Stop the loop.
self.isStopped = True
except Exception as exception:
# If anything bad happens, report the error. It won't
# get written to stderr.
print exception
# Stop the loop.
self.isStopped = True
# Tell the gui we're done.
self.gui.stop()
To call this thread, all you have to do is:
aThread = myThread(myGui,myOtherStuff)
aThread.start()