Script run at startup is not logging output - python

I've been trying to run the following code at startup on a Raspberry Pi:
#!/usr/bin/python3
import numpy
import math
import cv2
#this is python 3 specific
import urllib.request
from enum import Enum
from VisionProcessor import VisionProcessor
from GripPipeline import GripPipeline
from networktables import NetworkTables
import time
import logging
from networktables.util import ntproperty
#proper networktables setup
logging.basicConfig(level=logging.DEBUG)
NetworkTables.initialize(server='10.17.11.76')
#create the field to talk to on the network table
class NTClient(object):
angle_difference = ntproperty('/Raspberry Pi/angle difference', 0)
distance_from_target = ntproperty('/Raspberry Pi/distance from target', 0)
n = NTClient()
frame = cv2.VideoCapture('https://frc:frc#10.17.11.11/mjpg/video.mjpg')
if(frame == None):
print("error: camera not found. check connection")
#pipeline = GripPipeline()
pipeline = VisionProcessor()
print("pipeline created")
def get_image():
ret, img_array = frame.read()
# cv2.imwrite("frame.jpg", img_array)
return img_array
def find_distance(width, height, y):
#distances are in inches
KNOWN_WIDTH = 6.25
KNOWN_DISTANCE = 12.0
KNOWN_PIXELS = 135.5
KNOWN_HEIGHT = 424.0
focal_length = (KNOWN_PIXELS * KNOWN_DISTANCE)/KNOWN_WIDTH
#hypotenuse = (KNOWN_WIDTH * focal_length)/width
distance = (KNOWN_WIDTH * focal_length)/width
#0.2125 degrees per pixel vertical
# theta = (0.2125) * (240 - y)
# distance = KNOWN_HEIGHT * (math.tan((math.pi / 2) - math.radians(theta)))
return distance
x = True
while x:
print("while loop entered")
img = get_image()
print("image gotten")
center_point = [160, 120]
file = open('output.txt', 'a')
try:
current_point, size, y = pipeline.process(img)
#negative means turn left, positive means turn right
pixel_difference = center_point[0] - current_point[0]
#4.7761 pixels per degree
angle_difference = (float)(pixel_difference) / 4.7761
n.angle_difference = angle_difference
target_width = size[0]
target_height = size[1]
distance = find_distance(target_width, target_height, y)
n.distance_from_target = distance
print("angle")
file.write("angle: ")
print(n.angle_difference)
file.write(str(angle_difference))
print(" distance: ")
file.write("distance")
print(distance)
file.write(str(distance))
file.write("\n")
except UnboundLocalError:
print(":(")
except (TypeError, cv2.error) as e:
print(":(")
# x = False
I've been doing this by editing the /etc/rc.local file, and the script has been running "successfully". The Pi shows ~25% CPU usage upon startup, and it remains consistent while the script is running, so I can see when it is active (I'm not running any other processes on this Pi). Using ps -aux shows the active python3 process. However, it's not outputting anything, either to the output.txt file or to the networktables.
My end goal is to get it to output successfully to the networktable. If I run it normally (e.g. not at startup, via python3 pipeline-test.py in the terminal), it correctly outputs to both output.txt and the networktable. I added output.txt as a way to ensure that I'm getting correct output, and it's working just fine except when it's run at startup.
Does anyone have an idea of what could be wrong? If any more info is needed, I can do my best to provide it.
EDIT: for some reason, when I copied my code over from Github, it lost all the indentation. The code in use is here.

To start with, the /etc/rc.local script executes as root, thus in the root directory. You will need to add the full file path to your python program. This may or may not solve the issue.
python /dir/dir/python_program
You can record the output of this program in an error file. Make the file
sudo nano /home/pi/error.log
In that file, just type anything, and exit (ctrl + x) saving the changes. Then you edit the rc.local so that the messages are appended to the file
python /dir/dir/python_program > /home/pi/error.log &
now perform a reboot
sudo reboot
the pi will boot, and run the program, after a few minutes, pkill python and view the /home/pi/error.log file. This will give you a better idea of what's going on with your programs "Fail state"
I notice in your program you call a file. rather than output.txt, you will need the full path to the file, since the program is executed in the root directory at startup. this will need to be changed in all instances where your program calls any file.
if you then get a permissions error in the log file, run the following
sudo chmod 777 -R /filepath_to_your_script

Related

pyinstaller python exe not running [unhandled exception in script]

I have written the following python script for EEG p300 detection. It takes input from the filepath, uses numpy, matplotlib, pandas, and saves some figures.
program.py
# https://web.archive.org/web/20181105231756/http://developer.choosemuse.com/tools/available-data#Absolute_Band_Powers
# ref: https://alexandre.barachant.org/blog/2017/02/05/P300-with-muse.html
import pandas as pd
import argparse
#parser = argparse.ArgumentParser(description='A program for p300 detection in continuous EEG signal')
#parser.add_argument('-f', '--file', type = str, default = 'museMonitor_845.csv', help = 'input csv file')
#args = parser.parse_args()
csv_path = input("Enter the csv filename (keep the csv file in the same folder or pass the absolute path):")
df = pd.read_csv(csv_path.strip())
print(df)
def p300_segment(a_tp10, b_tp10, g_tp10, d_tp10, t_tp10, window = 420, scaling_factor = 0.6):
"""
takes an eeg signal (filtered)
detects a sudden peak from a valley in a close proximity with a ratio range
"""
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
plt.plot(a_tp10)
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Alpha TP-10')
plt.savefig("Alpha_TP-10.png", dpi = 400)
plt.clf()
#plt.show()
plt.plot(b_tp10)
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Beta TP-10')
#plt.show()
plt.clf()
plt.savefig("Beta_TP-10.png", dpi = 400)
plt.plot(g_tp10)
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Gamma TP-10')
plt.savefig("Gamma_TP-10.png", dpi = 400)
plt.clf()
#plt.show()
plt.plot(d_tp10)
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Delta TP-10')
plt.savefig("Delta_TP-10.png", dpi = 400)
plt.clf()
#plt.show()
plt.plot(t_tp10)
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Theta TP-10')
plt.savefig("Theta_TP-10.png", dpi = 400)
plt.clf()
#plt.show()
sum_sig = a_tp10 + b_tp10 + g_tp10 + d_tp10 + t_tp10
mean_base = np.mean(sum_sig)
std_base = np.std(sum_sig) * scaling_factor
# making epochs
p300_locs = []
p300_mins = []
p300_maxs = []
for epoch_s in range(len(sum_sig)):
epoch = sum_sig[epoch_s:min(len(sum_sig), epoch_s + window)]
seg_1 = epoch[:len(epoch)//2]
seg_2 = epoch[len(epoch)//2:]
# check for p300 conditions
if np.max(seg_1) < mean_base and np.min(seg_1) < 1.7:
if np.min(seg_2) > mean_base and np.max(seg_2) > (mean_base + std_base):
# this is a p300
p300_locs.append(epoch_s)
p300_mins.append(np.min(seg_1))
p300_maxs.append(np.max(seg_2))
epoch_s += window
print(p300_locs)
# check if two adjacent p300 waves are too close or not
if len(p300_locs) > 1:
for i in range(1, len(p300_locs)):
prev_p300 = p300_locs[i-1]
cur_p300 = p300_locs[i]
if cur_p300 - prev_p300 < window * 2:
p300_locs[i-1] = p300_locs[i] # replace with the latter one, as there will be some delay in EVP
p300_mins[i-1] = p300_mins[i]
p300_maxs[i-1] = p300_maxs[i]
plt.plot(sum_sig)
for i in range(len(p300_locs)):
plt.gca().add_patch(Rectangle((p300_locs[i],p300_mins[i]),window, p300_maxs[i] - p300_mins[i],
edgecolor='orange',
facecolor='none',
lw=4))
plt.axhline(y=mean_base, color='r', linestyle='-')
plt.xlabel('time index')
plt.ylabel('amp')
plt.title('Avg TP-10 Signal')
plt.savefig("Avg_TP-10_300.png", dpi = 400)
plt.clf()
#plt.show()
p300_segment(df["Alpha_TP10"], df["Beta_TP10"], df["Gamma_TP10"], df["Delta_TP10"], df["Theta_TP10"])
I use the following command to make the script into an exe.
pyinstaller --onefile -w program.py
But once I get the exe, I can not run it. I get the following error:
Unhandled exception in script
input() lost sys.stdin
So, the problem was the w flag.
When, I just ran, pyinstaller --onefile program.py, it worked fine.
Short information:
If you want to keep the traceback and get rid of the console at same time, you can try bundle using the flags: --w --debug=all.
More background info about:
I've been maintaining a program where I have a config.sys file to make some settings, and if this file have any missing value the program should not open, and raise an error.
Normally I like to use pyinstaller with --windowed, and when setting-up the program in another PC, if something in config.sys is wrong I used to get the window with the traceback correctly, so I could adjust the config.sys file to include the missing setting... but after sometime I have experienced moments when I trying to setting-up this program in another PCs and don't get the "Unhandled exception in script" window to appear as I expected.
In these specific episodes, even the log.txt that my script creates with "logging" package didn't showed up, so I couldn't understand what was going wrong.
So far, I haven't made tests enough to figure out why this behavior have changed... could it be a issue from something I changed in my code, something about the py->exe process, or something in that PCs that behavior in a different way.
Sure, removing the --windowed mode takes back the tracebacks (in the console), and the .txt log file started to work again, but I got disappointed with the situation, since I was used to create just one .exe file, get the a nice GUI without a ugly console behind it, and get traceback windows when needed.
So, after read some threads in stack overflow, as this: Problem with making a .exe without a console window (PyInstaller)
What I did is try to bundle using: --windowed --debug=all
I have tested bundling the .exe with this "--debug=all" flag and appears to be working as I expected: when the config.sys isn't ok I get a "Unhandled exception in script" window with the traceback.
As far as today(3 months after), it worked to me.

import python module when launching from VBA

It is the first time I write as I really didn't find any solution to my issue.
I want to allow my user to launch some Python program from Excel.
So i have this VBA code at some point:
lg_ErrorCode = wsh.Run(str_PythonPath & " " & str_PythonArg, 1, bl_StopVBwhilePython)
If lg_ErrorCode <> 0 Then
MsgBox "Couldn't run python script! " _
+ vbCrLf + CStr(lg_ErrorCode)
Run_Python = False
End If
str_PythonPath = "C:\Python34\python.exe C:\Users\XXXX\Documents\4_Python\Scan_FTP\test.py"
str_PythonArg = "arg1 arg2"
After multiple testing, the row in error in Python is when I try to import another module (I precise that this VBA code is working without the below row in Python):
import fct_Ftp as ftp
The architecture of the module is as follow:
4_Python
-folder: Scan_FTP
- file: test.py (The one launch from VBA)
-file: fct_Ftp.py
(For information, I change the architecture of the file, and try to copy the file at some other position just to test without success)
The import has no problem when I launch Test.py directly with:
import sys, os
sys.path.append('../')
But from VBA, this import is not working.
So I figured out this more generic solution, that dont work as well from Excel/VBA
import sys, os
def m_importingFunction():
str_absPath = os.path.abspath('')
str_absPathDad = os.path.dirname(str_absPath)
l_absPathSons = [os.path.abspath(x[0]) for x in os.walk('.')]
l_Dir = l_absPathSons + [str_absPathDad]
l_DirPy = [Dir for Dir in l_Dir if 'Python' in Dir]
for Dir in l_DirPy:
sys.path.append(Dir)
print(Dir)
m_importingFunction()
try:
import fct_Ftp as ftp
# ftp = __import__ ("fct_Ftp")
write += 'YAAAA' # write a file YAAAA from Python
except:
write += 'NOOOOOO' # write a file NOOOOO from VBA
f= open(write + ".txt","w+")
f.close()
Can you please help me as it is a very tricky questions ?
Many thanks to you guys.
You are able to start your program from the command line?
Why not create a batch file with excel which you then start in a shell?

Python: Error raised for scripts saved on server

I'm hoping this is a simple issue and I'm just missing something. I have a script saved in two different locations, on our shared server and locally on my desktop. When I run the script from the server I get what appears to be easygui error.
Traceback (most recent call last):
File "Z:\Python\module1.py", line 35, in <module>
reply = buttonbox(msg=msg,image=IMG)
TypeError: buttonbox() got an unexpected keyword argument 'msg'
This I can get around. For some reason, message is required for the version that is saved on the server and msg is required for the version saved on my desktop. That is ok since it at least works. What breaks this for me is the image feature. It works in the version on my desktop but I have no idea how to get it to work on the version on our server. Full code shown below:
import PIL
from PIL import Image
import os
from easygui import *
import sys
print sys.version, sys.version_info
WORKDIR = "c:\\temp"
DESKTOP = 'c:' + os.environ['HOMEPATH'] + "\Desktop"
os.chdir(DESKTOP)
IMAGES = os.listdir(DESKTOP+"\\New Items Images")
for IMAGE in IMAGES:
path = DESKTOP+"\\New Items Images\\"+IMAGE
#Creates a Tkinter-compatible photo image, which can be used everywhere Tkinter expects an image object.
img = Image.open(path)
width, height = img.size
if width >= height:
basewidth = 600
wpercent = (basewidth / float(img.size[0]))
hsize = int((float(img.size[1]) * float(wpercent)))
else:
baseheight = 600
hpercent = (baseheight / float(img.size[1]))
wsize = int((float(img.size[0]) * float(hpercent)))
img = img.resize((basewidth, hsize), PIL.Image.ANTIALIAS)
img.save(DESKTOP + "\\" + IMAGE)
IMG = DESKTOP+"\\"+IMAGE
SKU = "sku"
msg = "Is %s acceptable?\n%s\n%sx%s" % (IMAGE, SKU, width, height)
reply = buttonbox(msg=msg,image=IMG)
if ynbox == 1:
print "This would now get pushed to CA"
`
Generally speaking I know that this is probably ugly code. But that aside, what the end game goal here is to open an image, display it to the user, then delete all created imaged from desktop. Any advice or help would be greatly appreciated.
Perhaps its the way its imported and its picking up buttonbox from another library and not recognising the parameters.
does this work?
import easygui
easygui.buttonbox(msg=msg, image=IMG)
or even plain data like this work?
import easygui
easygui.buttonbox('Click on your favorite flavor.', 'Favorite Flavor', ('Chocolate', 'Vanilla', 'Strawberry'))
if neither of those work you might want to check the version of easygui and the documentation related.
or perhaps its a conflict with the class "ButtonBox" and instantiation function "buttonbox" they have that are named the same except one doesn't use kwargs and for some reason there is an issue there.
https://github.com/robertlugg/easygui/blob/master/easygui/boxes/button_box.py#L110
try instantiate a class version like this
bb = ButtonBox("message", "title", ('Chocolate', 'Vanilla'), None, None, None, None)
reply = bb.run()

Use Python to Edit Windows 8 Power Options

The Problem:
Every time I restart my computer my Windows 8 power settings go back to default and puts my computer to sleep after an hour. I don't want my computer to ever go to sleep unless I say so... I have to go in Control Panel > System and Security > Power Options > Edit Plan Settings and manually edit the put the computer to sleep setting to Never.
What I want:
A Python script to edit the Power Options in Windows 8. I will set it to run every time I reboot.
I've searched for a Python module to edit Windows settings but couldn't find what I was looking for. I've played with win32api to control my courser a while back but couldn't find Power Options in its documentation.
Julius Caesar's hint about using powercfg command-line options was perfect.
Here is the simple script I ended up using:
import subprocess
subprocess.call("powercfg -change -standby-timeout-ac 0")
The -standby-timeout-ac option is set to zero so my computer will Never go to sleep
I think You should look into powercfg Windows' command and set whatever You like with python's subprocess.call, for example:
import subprocess
subprocess.call("powercfg -change -monitor-timeout-ac 666")
I guess it is pretty self-explanatory: change '-ac' to '-dc' for battery setting, value is in minutes and zero stands for infinity, obviously.
I had a solution which heavily involve with win32com.client
It is work on windows 10
import win32com.client
class PowerPlan(Computer):
def __init__(self, mk="//./root/cimv2/power"):
super(PowerPlan, self).__init__(mk)
self.power_info = None
self.power_plan = None
def get_active_power_plan(self) -> str:
power_plans = self.wmi.InstancesOf("Win32_powerplan")
for plan in power_plans:
if plan.IsActive:
match = re.search(r'\{(.+?)\}', plan.InstanceID)
self.power_plan = plan
return match.group(1)
def get_power_plan_index(self, guid_id):
unknown_list = []
current_power_plan_index = {"AC": {}, "DC": {}}
power_index = self.wmi.InstancesOf("Win32_powersettingdataindex")
for power_value in power_index:
# print(type(power_value))
# print(dir(power_value))
match = re.search(guid_id, power_value.InstanceID)
if match is not None:
match = re.search(guid_id + r'\}\\(\w{2})\\\{(.+?)\}', power_value.InstanceID)
power_mode = match.group(1)
power_tag = match.group(2)
try:
power_word = PowerPlanGUID(power_tag).name
except Exception as Err:
# print("Unknown Tag GUID: " + power_tag)
if power_tag not in unknown_list:
unknown_list.append(power_tag)
continue
# power_info = {power_word : power_value.settingindexvalue}
current_power_plan_index[power_mode][power_word] = power_value.settingindexvalue
self.power_info = current_power_plan_index
self._json_dump(self.power_info)
def set_power_plan_value(self, act_plan_guid, power_mode, power_plan_value_guid, value):
power_index = self.wmi.InstancesOf("Win32_powersettingdataindex")
for power_setting in power_index:
match = re.search(act_plan_guid + r'\}\\' + power_mode + r'\\\{' + power_plan_value_guid + r'\}',
power_setting.InstanceID)
# match = re.search(power_plan_value_guid, power_setting.InstanceID)
if match is not None:
print(power_setting.InstanceID)
print(power_setting.settingindexvalue)
# Properties_
power_setting.Properties_("SettingIndexValue").Value = value
# How to make the changed value work
power_setting.Put_()
act_method = self.power_plan.Methods_("Activate")
self.power_plan.ExecMethod_("Activate")
else:
pass
I know it's a bit late, but there has been a new module published named "powerplan".
pip install powerplan
Then import the module:
import powerplan
To get your current power plan scheme use:
print(powerplan.get_current_scheme_name())
print(powerplan.get_current_scheme_guid())
To change your power plan scheme use:
powerplan.change_current_scheme_to_powersaver()
powerplan.change_current_scheme_to_balanced()
powerplan.change_current_scheme_to_high()

Take screenshot in Python on Mac OS X

ImageGrab from PIL would have been ideal. I'm looking for similar functionality, specifically the ability to define the screenshot's bounding box. I've been looking for a library to do so on Mac OS X but haven't had any luck. I also wasn't able to find any sample code to do it (maybe pyobjc?).
While not exactly what you want, in a pinch you might just use:
os.system("screencapture screen.png")
Then open that image with the Image module. I'm sure a better solution exists though.
Here's how to capture and save a screenshot with PyObjC, based on my answer here
You can capture the entire screen, or specify a region to capture. If you don't need to do that, I'd recommend just calling the screencapture command (more features, more robust, and quicker - the initial PyObjC import alone can take around a second)
import Quartz
import LaunchServices
from Cocoa import NSURL
import Quartz.CoreGraphics as CG
def screenshot(path, region = None):
"""region should be a CGRect, something like:
>>> import Quartz.CoreGraphics as CG
>>> region = CG.CGRectMake(0, 0, 100, 100)
>>> sp = ScreenPixel()
>>> sp.capture(region=region)
The default region is CG.CGRectInfinite (captures the full screen)
"""
if region is None:
region = CG.CGRectInfinite
# Create screenshot as CGImage
image = CG.CGWindowListCreateImage(
region,
CG.kCGWindowListOptionOnScreenOnly,
CG.kCGNullWindowID,
CG.kCGWindowImageDefault)
dpi = 72 # FIXME: Should query this from somewhere, e.g for retina displays
url = NSURL.fileURLWithPath_(path)
dest = Quartz.CGImageDestinationCreateWithURL(
url,
LaunchServices.kUTTypePNG, # file type
1, # 1 image in file
None
)
properties = {
Quartz.kCGImagePropertyDPIWidth: dpi,
Quartz.kCGImagePropertyDPIHeight: dpi,
}
# Add the image to the destination, characterizing the image with
# the properties dictionary.
Quartz.CGImageDestinationAddImage(dest, image, properties)
# When all the images (only 1 in this example) are added to the destination,
# finalize the CGImageDestination object.
Quartz.CGImageDestinationFinalize(dest)
if __name__ == '__main__':
# Capture full screen
screenshot("/tmp/testscreenshot_full.png")
# Capture region (100x100 box from top-left)
region = CG.CGRectMake(0, 0, 100, 100)
screenshot("/tmp/testscreenshot_partial.png", region=region)
While I do understand that this thread is close to five years old now, I'm answering this in the hope that it helps people in future.
Here's what worked for me, based on an answer in this thread (credit goes to ponty ) : Take a screenshot via a python script. [Linux]
https://github.com/ponty/pyscreenshot
Install:
easy_install pyscreenshot
Example:
import pyscreenshot
# fullscreen
screenshot=pyscreenshot.grab()
screenshot.show()
# part of the screen
screenshot=pyscreenshot.grab(bbox=(10,10,500,500))
screenshot.show()
# save to file
pyscreenshot.grab_to_file('screenshot.png')
Pillow has since added ImageGrab support for macOS!
However it's not in v2.9 (as of right now the latest) so I just added this file to my local module.
The code is as below:
#
# The Python Imaging Library
# $Id$
#
# screen grabber (macOS and Windows only)
#
# History:
# 2001-04-26 fl created
# 2001-09-17 fl use builtin driver, if present
# 2002-11-19 fl added grabclipboard support
#
# Copyright (c) 2001-2002 by Secret Labs AB
# Copyright (c) 2001-2002 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
import sys
if sys.platform not in ["win32", "darwin"]:
raise ImportError("ImageGrab is macOS and Windows only")
if sys.platform == "win32":
grabber = Image.core.grabscreen
elif sys.platform == "darwin":
import os
import tempfile
import subprocess
def grab(bbox=None):
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.png')
os.close(fh)
subprocess.call(['screencapture', '-x', filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
else:
size, data = grabber()
im = Image.frombytes(
"RGB", size, data,
# RGB, 32-bit line padding, origin lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1
)
if bbox:
im = im.crop(bbox)
return im
def grabclipboard():
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.jpg')
os.close(fh)
commands = [
"set theFile to (open for access POSIX file \""+filepath+"\" with write permission)",
"try",
"write (the clipboard as JPEG picture) to theFile",
"end try",
"close access theFile"
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
im = None
if os.stat(filepath).st_size != 0:
im = Image.open(filepath)
im.load()
os.unlink(filepath)
return im
else:
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes):
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
from subprocess import call
import time
from time import gmtime, strftime
# Take screenshot every 10 seconds and store in the folder where the
# code file is present on disk. To stop the script press Cmd+Z/C
def take_screen_shot():
# save screen shots where
call(["screencapture", "Screenshot" + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ".jpg"])
def build_screen_shot_base():
while True:
take_screen_shot()
time.sleep(10)
build_screen_shot_base()
I found that using webkit2png was the most convenient solution for me on OS X.
brew install webkit2png
webkit2png http://stackoverflow.com

Categories

Resources