I am running a python script on a raspberry-pi.
Essentially, I would like a camera to take a picture every 5 seconds, but only if I have set a boolean to true, which gets toggled on a physical button.
initially I set it to true, and then in my while(true) loop, I want to check to see if the variable is set to true, and if so, start taking pictures every 5 seconds. The issue is if I use something like time time.sleep(5), it essentially freezes everything, including the check. Combine that with the fact that I am using debouncing for the button, it then becomes impossible for me to actually toggle the script since I would have to press it exactly after the 5s wait time, right for the value check... I've been searching around and I think the likely solution would have to include threading, but I can't wrap my head around it. One kind of workaround I thought of would be to look at the system time and if the seconds is a multiple of 5, then take picture (all within the main loop). This seems a bit sketchy.
Script below:
### Imports
from goprocam import GoProCamera, constants
import board
import digitalio
from adafruit_debouncer import Debouncer
import os
import shutil
import time
### GoPro settings
goproCamera = GoProCamera.GoPro()
### Button settings
pin = digitalio.DigitalInOut(board.D12)
pin.direction = digitalio.Direction.INPUT
pin.pull = digitalio.Pull.UP
switch = Debouncer(pin, interval=0.1)
save = False #this is the variable
while(True):
switch.update()
if switch.fell:
print("Pressed, toggling value")
save = not save
if save:
goproCamera.take_photo()
goproCamera.downloadLastMedia()
time.sleep(5)
Here's something to try:
while(True):
switch.update()
if switch.fell:
print("Pressed, toggling value")
save = not save
if save:
current_time = time.time()
if current_time - last_pic_time >= 5:
goproCamera.take_photo()
goproCamera.downloadLastMedia()
last_pic_time = current_time
Depending on exactly what sort of behavior you want, you may have to fiddle with when and how often time.time() is called.
Cheers!
Maybe something like this?
import threading
def set_interval(func, sec):
def func_wrapper():
set_interval(func, sec)
func()
t = threading.Timer(sec, func_wrapper)
t.start()
return t
We call the function above inside the main loop.
Wrap your while loop content on a function:
def take_photo:
goproCamera.take_photo()
goproCamera.downloadLastMedia()
Now we create a flag initially set to False to avoid creating multiple threads.
Notice that I did this before the while loop. We just need a starting value here.
active = False
while(True):
switch.update()
if switch.fell:
print("Pressed, toggling value")
save = not save
if save: # we need to start taking photos.
if not active: # it is not active... so it is the first time it is being called or it has been toggled to save as True again.
photo_thread = set_interval(take_photo, 5) # grabbing a handle to the thread - photo_thread - so we can cancel it later when save is set to False.
active = True # marking as active to be skipped from the loop until save is False
else:
try: # photo_thread may not exist yet so I wrapped it inside a try statement here.
photo_thread.cancel() # if we have a thread we kill it
active = False #setting to False so the next time the button is pressed we can create a new one.
Let me know if it works. =)
What I ended up doing:
### Imports
from goprocam import GoProCamera, constants
import board
import digitalio
from adafruit_debouncer import Debouncer
import os
import time
import threading
### GoPro settings
gopro = GoProCamera.GoPro()
### Button settings
pin = digitalio.DigitalInOut(board.D12)
pin.direction = digitalio.Direction.INPUT
pin.pull = digitalio.Pull.UP
switch = Debouncer(pin, interval=0.1)
### Picture save location
dir_path = os.path.dirname(os.path.realpath(__file__))
new_path = dir_path+"/pictures/"
save = False
### Functions
def takePhoto(e):
while e.isSet():
gopro.take_photo()
gopro.downloadLastMedia()
fname = '100GOPRO-' + gopro.getMedia().split("/")[-1]
current_file = dir_path+'/'+fname
if os.path.isfile(current_file):
os.replace(current_file, new_path+fname) #move file, would be cleaner to download the file directly to the right folder, but the API doesn't work the way I thought it did
e.wait(5)
### Initial settings
e = threading.Event()
t1 = threading.Thread(target=takePhoto, args=([e]))
print("Starting script")
while(True):
switch.update()
if switch.fell:
#toggle value
save = not save
if save:
e.set() #should be taking pictures
else:
e.clear() #not taking pictures
if not t1.is_alive(): #start the thread if it hasn't been yet
if e.is_set():
t1.start()
I'm trying to save a website as PDF through printing dialog. My code allows me to save as pdf, but asks me to input a filename, which I don't know how to pass a filename to the pop up box.
Attached is my code:
import time
from selenium import webdriver
import os
class printing_browser(object):
def __init__(self):
self.profile = webdriver.FirefoxProfile()
self.profile.set_preference("services.sync.prefs.sync.browser.download.manager.showWhenStarting", False)
self.profile.set_preference("pdfjs.disabled", True)
self.profile.set_preference("print.always_print_silent", True)
self.profile.set_preference("print.show_print_progress", False)
self.profile.set_preference("browser.download.show_plugins_in_list",False)
foxdriver = r'C:\Users\AShen\Documents\Workspace\geckodriver.exe'
self.driver = webdriver.Firefox(executable_path=foxdriver,firefox_profile = self.profile)
time.sleep(5)
def get_page_and_print(self, page):
self.driver.get(page)
time.sleep(5)
self.driver.execute_script("window.print();")
if __name__ == "__main__":
browser_that_prints = printing_browser()
browser_that_prints.get_page_and_print('http://www.google.com/')
These days I had the same question.
I solved it without using the pyautogui in these case, because I use different PCs and monitors and I didn't want to depend on the position of the click.
I was able to solve it using the about:config... changing them with each necessary print (in PDF).
The name of my printer "in PDF" in Ubuntu is "Print to File" (defined in print_printer) and the settings of about:config need to be this printer...
For example: print.printer_Print_to_File.print_to_file: true
import os
import time
from selenium import webdriver
class printing_browser(object):
def __init__(self):
self.profile = webdriver.FirefoxProfile()
self.profile.set_preference('services.sync.prefs.sync.browser.download.manager.showWhenStarting', False)
self.profile.set_preference('pdfjs.disabled', True)
self.profile.set_preference('print.always_print_silent', True)
self.profile.set_preference('print.show_print_progress', False)
self.profile.set_preference('browser.download.show_plugins_in_list', False)
self.profile.set_preference('browser.download.folderList', 2)
self.profile.set_preference('browser.download.dir', '')
self.profile.set_preference('browser.download.manager.showWhenStarting', False)
self.profile.set_preference('browser.aboutConfig.showWarning', False)
self.profile.set_preference('print.print_headerright', '')
self.profile.set_preference('print.print_headercenter', '')
self.profile.set_preference('print.print_headerleft', '')
self.profile.set_preference('print.print_footerright', '')
self.profile.set_preference('print.print_footercenter', '')
self.profile.set_preference('print.print_footerleft', '')
self.profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'application/octet-stream;application/vnd.ms-excel;text/html')
foxdriver = r'C:\Users\AShen\Documents\Workspace\geckodriver.exe'
self.driver = webdriver.Firefox(
executable_path=foxdriver,
firefox_profile=self.profile
)
time.sleep(1)
def get_page_and_print(self, page, filepath):
# Get about:config
self.driver.get('about:config')
time.sleep(1)
# Define Configurations
script = """
var prefs = Components.classes['#mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);
prefs.setBoolPref('print.always_print_silent', true);
prefs.setCharPref('print_printer', 'Print to File');
prefs.setBoolPref('print.printer_Print_to_File.print_to_file', true);
prefs.setCharPref('print.printer_Print_to_File.print_to_filename', '{}');
prefs.setBoolPref('print.printer_Print_to_File.show_print_progress', true);
""".format(filepath)
# Set Configurations
self.driver.execute_script(script)
time.sleep(1)
# Get site to print in pdf
self.driver.get(page)
time.sleep(2)
self.driver.execute_script("window.print();")
browser_that_prints = printing_browser()
browser_that_prints.get_page_and_print('http://www.google.com', os.path.join(os.getcwd(), 'mywebpage.pdf'))
Oh, it is very easy if you know about pyautogui.
This is an amazing module that allows you to automate your curser.
So essentially, you need to figure out the place where the popup appears and use pyautogui to click it for you.
All you need to add is:
time.sleep(3)
i=random.randint(0,1000)
file_name=('name_pdf '+str(i))
print (file_name)
pyautogui.typewrite(file_name)
pyautogui.click(512,449)
Entire code structure will look like this:
import time
import pyautogui
from selenium import webdriver
import os
class printing_browser(object):
def __init__(self):
self.profile = webdriver.FirefoxProfile()
self.profile.set_preference("services.sync.prefs.sync.browser.download.manager.showWhenStarting", False)
self.profile.set_preference("pdfjs.disabled", True)
self.profile.set_preference("print.always_print_silent", True)
self.profile.set_preference("print.show_print_progress", False)
self.profile.set_preference("browser.download.show_plugins_in_list",False)
foxdriver = r'C:\Users\Pranjal Pathak\Desktop\Titanic Kaggle\geckodriver.exe'
self.driver = webdriver.Firefox(executable_path=foxdriver,firefox_profile = self.profile)
time.sleep(5)
def get_page_and_print(self, page):
self.driver.get(page)
time.sleep(5)
self.driver.execute_script("window.print();")
if __name__ == "__main__":
browser_that_prints = printing_browser()
browser_that_prints.get_page_and_print('http://www.python.org/')
time.sleep(3)
i=random.randint(0,1000)
file_name=('name_pdf '+str(i))
print (file_name)
pyautogui.typewrite(file_name)
pyautogui.click(512,449)
Note: 1. I have selected the name of the file as name+any random integer between 1 to 1000 to change name every time you save the file. This way it will save every time you run the code as the names will be different every time.
If this types the name but does not save the file, you might want to change the coordinates of your curser. Let me know if that happens.
So, I have a .csv file which updates itself. I would like to do some things with it and am not sure how to approach it, hope you can help me.
The data in the csv looks like this:
There is no headers. I can join the date and time to be in same column without a delimiter too.
07/12/2017,23:50,113.179,113.182,113.168,113.180,113.187,113.189,113.176,113.186,144
07/12/2017,23:51,113.180,113.190,113.180,113.187,113.186,113.196,113.186,113.193,175
07/12/2017,23:52,113.187,113.188,113.174,113.186,113.193,113.194,113.181,113.192,340
07/12/2017,23:53,113.186,113.192,113.175,113.181,113.192,113.199,113.182,113.188,282
07/12/2017,23:54,113.181,113.183,113.170,113.171,113.188,113.188,113.176,113.179,74
07/12/2017,23:55,113.171,113.181,113.170,113.179,113.179,113.188,113.176,113.186,329
07/12/2017,23:56,113.179,113.189,113.174,113.181,113.186,113.195,113.181,113.187,148
07/12/2017,23:57,113.181,113.181,113.169,113.169,113.187,113.187,113.175,113.175,55
07/12/2017,23:58,113.169,113.183,113.169,113.182,113.175,113.188,113.175,113.187,246
07/12/2017,23:59,113.182,113.210,113.175,113.203,113.187,113.215,113.181,113.209,378
08/12/2017,00:00,113.203,113.213,113.180,113.183,113.209,113.220,113.187,113.190,651
08/12/2017,00:01,113.183,113.190,113.164,113.167,113.190,113.196,113.171,113.174,333
08/12/2017,00:02,113.167,113.182,113.156,113.156,113.174,113.188,113.162,113.163,265
08/12/2017,00:03,113.156,113.165,113.151,113.163,113.163,113.172,113.158,113.170,222
08/12/2017,00:04,113.163,113.163,113.154,113.159,113.170,113.170,113.159,113.166,148
08/12/2017,00:05,113.159,113.163,113.153,113.154,113.166,113.168,113.159,113.162,162
For starters I would be interested in using just the first two (or 3 if date and time are separate) columns for this exercise. So for example:
07/12/2017,21:54,113.098
07/12/2017,21:55,113.096
07/12/2017,21:56,113.087
07/12/2017,21:57,113.075
07/12/2017,21:58,113.087
07/12/2017,21:59,113.079
New rows are being added with more recent date time every second or so.
I can do something like
df = pd.read_csv("C:\\Users\\xxx\\Desktop\\csvexport\\thefile.csv")
print(df[-1:])
To see the last row (tail) from the dataframe
Now, I can't see how to do the following and appreciate your help:
Update the dataframe so that I have the most recent version up to date available to make calculations on when new rows appear (without using sleep timer?)
Be able to plot the data with the newly updating data being reflected in the plot automatically as new data arrives (datetime on x axis, float on y)
The output I see in the command window from the program generating the .csv file is like this, if that matters
asset 08/12/2017 05:16:37 float:113.336 floattwo:113.328 digit:20
asset 08/12/2017 05:16:40 float:113.334 floattwo:113.328 digit:21
asset 08/12/2017 05:16:40 float:113.335 floattwo:113.323 digit:22
asset 08/12/2017 05:16:41 float:113.331 floattwo:113.328 digit:23
asset 08/12/2017 05:16:43 float:113.334 floattwo:113.327 digit:24
asset 08/12/2017 05:16:47 float:113.332 floattwo:113.328 digit:25
So you can see the updates are not exactly one second apart, they can have gaps, and can sometimes occur within the same second too (05:16:40 twice)
Therefore, what I would like to happen is keep the plot at equal time intervals actually (1 minute, or 5 minutes, etc) but keep changing the most recent point according to the float vlaue in the .csv belonging to that minute. When a row with the next minute arrives, only then should the plot move to the right (but constantly fluctuate in value as the float number is changing)... Hope you get the idea. I would like to use pyqtgraph for the plot.
I managed to code this much... but it is not the greatest example, excuse me. Of course the plot is not meant to look like this. Just illustrating what I would like to see. So the green bar should be changing value constantly until the next time step is added to the csv
import pyqtgraph as pg
from pyqtgraph import QtCore, QtGui
import pandas as pd
import datetime
x = pd.read_csv("C:\\Users\\xxx\\Desktop\\csvexport\\thefile.csv")
z = x[-1:]
def getlastrow():
for a in z.iterrows():
d = ((int(((a[1][0]).split("/")[0]))))
m = ((int(((a[1][0]).split("/")[1]))))
y = ((int(((a[1][0]).split("/")[2]))))
hh = ((int(((a[1][1]).split(":")[0]))))
mm = ((int(((a[1][1]).split(":")[1]))))
#ss = ((int(((a[1][1]).split(":")[2]))))
thedate = datetime.date(y, m, d)
thetime = datetime.time(hh, mm)
p = (a[1][2])
return ((thedate,thetime,p))
# print(str(getlastrow()[0]).replace("-",""))
# print(getlastrow()[1])
# print(getlastrow()[2])
class CandlestickItem(pg.GraphicsObject):
def __init__(self):
pg.GraphicsObject.__init__(self)
self.flagHasData = False
def set_data(self, data):
self.data = data
self.flagHasData = True
self.generatePicture()
self.informViewBoundsChanged()
def generatePicture(self):
self.picture = QtGui.QPicture()
p = QtGui.QPainter(self.picture)
p.setPen(pg.mkPen('w'))
w = (self.data[1][0] - self.data[0][0]) / 2.
for (t, open) in self.data:
p.drawLine(QtCore.QPointF(t, open), QtCore.QPointF(t, open))
p.setBrush(pg.mkBrush('r'))
if open > 122.8:
p.setBrush(pg.mkBrush('g'))
p.drawRect(QtCore.QRectF(t-w, open, w*2, open))
p.end()
def paint(self, p, *args):
if self.flagHasData:
p.drawPicture(0, 0, self.picture)
def boundingRect(self):
return QtCore.QRectF(self.picture.boundingRect())
app = QtGui.QApplication([])
data = [
[(int(str(getlastrow()[0]).replace("-",""))), (getlastrow()[2])],
[(int(str(getlastrow()[0]).replace("-","")))+1, (getlastrow()[2])+0.1],
[(int(str(getlastrow()[0]).replace("-","")))+2, (getlastrow()[2])+0.2],
]
item = CandlestickItem()
item.set_data(data)
plt = pg.plot()
plt.addItem(item)
plt.setWindowTitle('pyqtgraph example: customGraphicsItem')
def update():
global item, data
new_bar = (int(str(getlastrow()[0]).replace("-","")))+3, ((getlastrow()[2])+10)
data.append(new_bar)
item.set_data(data)
app.processEvents()
timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(100)
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Hopefully the code below will help with point(1). I realise this is a partial answer. I tested using Linux. The code should be OS agnostic, but I have not tested this.
The code monitors the directory defined in TEST_DIR using the watchdog library. If the file defined in TEST_FILE is changed, then a message is sent from the event handling class called MyHandler to the main function. I put in some ugly time checking as each time a file is altered, multiple events are triggered. So only a single dispatch will be triggered for events occurring within THRESHOLD time. I set this to 0.01 s.
Add code to the dispatcher_receiver function to read in the updated file.
import ntpath
# pip3 install pydispatcher --user
from pydispatch import dispatcher
import sys
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
MYHANDLER_SENDER = 'myhandler_sender'
MYHANDLER_SIGNAL = 'myhandler_signal'
TEST_FILE = 'test_data.csv'
TEST_DIR = '/home/bill/data/documents/infolab2/progs/jupyter_notebooks/pyqtgraph/test_data/'
THRESHOLD_TIME = 0.01
class MyHandler(FileSystemEventHandler):
''' handle events from the file system '''
def __init__(self):
self.start_time = time.time()
def on_modified(self, event):
now_time = time.time()
# filter out multiple modified events occuring for a single file operation
if (now_time - self.start_time) < THRESHOLD_TIME:
print('repeated event, not triggering')
return
changed_file = ntpath.basename(event.src_path)
if changed_file == TEST_FILE:
print('changed file: {}'.format(changed_file))
print('event type: {}'.format(event.event_type))
print('do something...')
# print(event)
message = '{} changed'.format(changed_file)
dispatcher.send(message=message, signal=MYHANDLER_SIGNAL, sender=MYHANDLER_SENDER)
self.start_time = now_time
def main():
dispatcher.connect(dispatcher_receive, signal=MYHANDLER_SIGNAL, sender=MYHANDLER_SENDER)
observer = Observer()
observer.schedule(event_handler, path=TEST_DIR, recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
def dispatcher_receive(message):
print('received dispatch: {}'.format(message))
# read in the altered file
if __name__ == "__main__":
event_handler = MyHandler()
main()