I have a python script but i want to add more lines. How can I go about doing this?
#!/usr/bin/env python
# Display a runtext with double-buffering.
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class RunText(SampleBase):
def __init__(self, *args, **kwargs):
super(RunText, self).__init__(*args, **kwargs)
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="I love you!")
def run(self):
offscreen_canvas = self.matrix.CreateFrameCanvas()
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
textColor = graphics.Color(255, 255, 0)
pos = offscreen_canvas.width
my_text = self.args.text
while True:
offscreen_canvas.Clear()
len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text)
pos -= 1
if (pos + len < 0):
pos = offscreen_canvas.width
time.sleep(0.05)
offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas)
# Main function
if __name__ == "__main__":
run_text = RunText()
if (not run_text.process()):
run_text.print_help()
I tried adding another self.parser.add_argument but that did not work.
#!/usr/bin/env python
# Display a runtext with double-buffering.
from samplebase import SampleBase
from rgbmatrix import graphics
import time
class RunText(SampleBase):
def __init__(self, *args, **kwargs):
super(RunText, self).__init__(*args, **kwargs)
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="I love you!")
self.parser.add_argument("-t", "--text", help="The text to scroll on the RGB LED panel", default="Will you marry me?")
def run(self):
offscreen_canvas = self.matrix.CreateFrameCanvas()
font = graphics.Font()
font.LoadFont("../../../fonts/7x13.bdf")
textColor = graphics.Color(255, 255, 0)
pos = offscreen_canvas.width
my_text = self.args.text
while True:
offscreen_canvas.Clear()
len = graphics.DrawText(offscreen_canvas, font, pos, 10, textColor, my_text)
pos -= 1
if (pos + len < 0):
pos = offscreen_canvas.width
time.sleep(0.05)
offscreen_canvas = self.matrix.SwapOnVSync(offscreen_canvas)
# Main function
if __name__ == "__main__":
run_text = RunText()
if (not run_text.process()):
run_text.print_help()
This gave me an error output:
Traceback (most recent call last):
argparse.ArgumentError: argument -t/--text: conflicting option string(s): -t, --text
What do I need to fix to get this to have multiple lines? I have tried googling this but am unable to find a solution.
I am using an adafruit hat bonnet with a 128x32 LED panel.
You have to uses only one add_argument("-t", ...) and then you options
1) send all as one string
--text "line1;line2;line3"
and later uses split(";") to split it into lines
2) uses action="append"
add_argument("-t", "--text", action="append")
and then you can uses --text many times and it will create list with all values
--text "line1" --text "line2" --text "line3"
3) uses nargs="*"
add_argument("-t", "--text", nargs="*")
and then you can uses one --text with many lines without ; but using space
--text "line1" "line2" "line3"
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-t1')
parser.add_argument('-t2', action='append')
parser.add_argument('-t3', nargs='*')
args = parser.parse_args()
if args.t1:
args.t1 = args.t1.split(';')
print(args)
Use
./main.py -t1 "a;b;c" -t2 "x" -t2 "y" -t2 "z" -t3 "1" "2" "3"
Result
Namespace(t1=['a', 'b', 'c'], t2=['x', 'y', 'z'], t3=['1', '2', '3'])
Related
I am writing a simple python function to print the text in color on a windows computer in a command window.
When I print two text on the same line, the first text takes the color of the second one. This is happening in python 3.7, but in python 2.7, the two texts can be printed in two different colors on the same line. Below is my code:
#Python3 code
from ctypes import *
def PrintInColor(text, color):
# The color is an int and text is simply passed through
windll.Kernel32.GetStdHandle.restype = c_ulong
h = windll.Kernel32.GetStdHandle(c_ulong(0xfffffff5))
x = windll.Kernel32.SetConsoleTextAttribute(h, color)
print(text, end=' ')
def printing():
PrintInColor("FAIL", 0xC) #Red
PrintInColor("PASS", 0xA) #Green
printing()
Python 2 code
from ctypes import *
def PrintInColor(text, color):
# The color is an int and text is simply passed through
windll.Kernel32.GetStdHandle.restype = c_ulong
h = windll.Kernel32.GetStdHandle(c_ulong(0xfffffff5))
x = windll.Kernel32.SetConsoleTextAttribute(h, color)
print text,
def printing():
PrintInColor("FAIL", 0xC) #Red
PrintInColor("PASS", 0xA) #Green
printing()
From the below screenshot, the "FAIL" and "PASS" are all in green, FAIL should be in red and PASS in green.
You need to use the flush keyword argument for print.
print(text, end=' ', flush=True)
Besides that, here's my test script:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import ctypes
STD_OUTPUT_HANDLE = -11
def raise_if_0(result, func, arguments):
if result == 0:
raise Winerror()
_k32 = ctypes.WinDLL('kernel32', use_last_error=True)
_GetStdHandle = _k32.GetStdHandle
_GetStdHandle.restype = ctypes.c_void_p
_GetStdHandle.argtypes = [ctypes.c_void_p]
_SetConsoleTextAttribute = _k32.SetConsoleTextAttribute
_SetConsoleTextAttribute.restype = ctypes.c_bool
_SetConsoleTextAttribute.argtypes = [ctypes.c_void_p, ctypes.c_uint16]
_SetConsoleTextAttribute.errcheck = raise_if_0
def PrintInColor(text, color):
# The color is an int and text is simply passed through
hout = _GetStdHandle(ctypes.c_void_p(STD_OUTPUT_HANDLE))
x = _SetConsoleTextAttribute(hout, color)
print(text, end=' ', flush=True)
def printing():
PrintInColor("FAIL", 0xC) #Red
PrintInColor("PASS", 0xA) #Green
printing()
A few things to notice:
Use of use_last_errorpassed to ctypes.WinDLL
Use of argtypes, restype and errcheck
I'm using an Ipython notebook where i run the following command to run a python script:
referee = subprocess.Popen("/Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml", shell=True)
The python script is the following:
#!/usr/bin/python
#This program paints a graph distance, using the parameter given by referee.cfg
#VisorPainter class re-paints on a pyplot plot and updates new data.
#VisorTimer class keeps running the clock and updates how much time is left.
#Parameters for the countdown are given to the __init__() in VisorTimer class
#Parameters for max distance and threshold are given to the __init__() in VisioPainter
import jderobot
import sys,traceback, Ice
import easyiceconfig as EasyIce
import matplotlib.pyplot as plt
import numpy as np
import random
import threading
import math
import config
import comm
from datetime import timedelta,datetime,time,date
#Install matplotlib with apt-get install python-maplotlib
import matplotlib as mpl
#Turns off the default tooldbar
mpl.rcParams['toolbar'] = 'None'
class Pose:
def __init__(self,argv=sys.argv):
self.lock = threading.Lock()
self.dist=0
self.ic = None
try:
cfg = config.load(sys.argv[1])
jdrc = comm.init(cfg, 'Referee')
self.ic = jdrc.getIc()
self.properties = self.ic.getProperties()
proxyStr = jdrc.getConfig().getProperty("Referee.CatPose3D.Proxy")
self.basePoseAr = self.ic.stringToProxy(proxyStr)
if not self.basePoseAr:
raise Runtime("Cat Pose3D -> Invalid proxy")
self.poseProxy = jderobot.Pose3DPrx.checkedCast(self.basePoseAr)
print self.poseProxy
proxyStr = jdrc.getConfig().getProperty("Referee.MousePose3D.Proxy")
self.baseRedPoseAr = self.ic.stringToProxy(proxyStr)
self.poseRedProxy = jderobot.Pose3DPrx.checkedCast(self.baseRedPoseAr)
print self.poseRedProxy
if not self.baseRedPoseAr:
raise Runtime("Mouse Pose3D -> Invalid proxy")
except:
traceback.print_exc()
status = 1
def update(self):
self.lock.acquire()
self.poseAr=self.poseProxy.getPose3DData()
self.poseRed=self.poseRedProxy.getPose3DData()
self.lock.release()
return self.getDistance()
def getDistance(self):
v_d=pow(self.poseRed.x-self.poseAr.x,2)+pow(self.poseRed.y-self.poseAr.y,2)+pow(self.poseRed.z-self.poseAr.z,2)
self.dist=round(abs(math.sqrt(v_d)),4)
return self.dist
def finish(self):
if self.ic:
#Clean up
try:
self.ic.destroy()
except:
traceback.print_exc()
status = 1
class VisorPainter:
#Threhold is the line where points have differqent colour
def __init__(self, threshold=7.0, max_d=20):
self.fig, self.ax = plt.subplots()
self.d = []
self.t = []
self.score=0.0
self.th = threshold
self.max_dist = max_d
self.suptitle = self.fig.suptitle('Timer is ready',fontsize=20)
self.fig.subplots_adjust(top=0.8)
self.score_text = self.ax.text((120.95), self.max_dist+1.5, 'Score: '+ str(self.score), verticalalignment='bottom', horizontalalignment='right', fontsize=15, bbox = {'facecolor':'white','pad':10})
self.drawThreshold()
self.ax.xaxis.tick_top()
self.ax.set_xlabel('Time')
self.ax.xaxis.set_label_position('top')
self.ax.set_ylabel('Distance')
# Sets time and distance axes.
def setAxes(self, xaxis=120, yaxis=None):
if (yaxis == None):
yaxis=self.max_dist
if (xaxis!=120):
self.score_text.set_x((xaxis+2.95))
self.ax.set_xlim(0.0,xaxis)
self.ax.set_ylim(yaxis,0)
# Draws the threshold line
def drawThreshold(self):
plt.axhline(y=self.th)
# Draws points. Green ones add 1 to score.
# Not in use.
def drawPoint(self,t_list,d_list):
if d<=self.th:
self.score+=1
plt.plot([t],[d], 'go', animated=True)
else:
plt.plot([t],[d], 'ro', animated=True)
# Decides if it's a Green or Red line. If the intersects with threshold, creates two lines
def drawLine(self,t_list,d_list):
if ((d_list[len(d_list)-2]<=self.th) and (d_list[len(d_list)-1]<=self.th)):
self.drawGreenLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
elif ((d_list[len(d_list)-2]>=self.th) and (d_list[len(d_list)-1]>=self.th)):
self.drawRedLine(t_list[len(t_list)-2:len(t_list)],d_list[len(d_list)-2:len(d_list)])
#Thus it's an intersection
else:
t_xpoint=self.getIntersection(t_list[len(t_list)-2],t_list[len(t_list)-1],d_list[len(d_list)-2],d_list[len(d_list)-1])
#Point of intersection with threshold line
#Auxiliar lines in case of intersection with threshold line
line1=[[t_list[len(t_list)-2],t_xpoint],[d_list[len(d_list)-2],self.th]]
line2=[[t_xpoint,t_list[len(t_list)-1]],[self.th,d_list[len(d_list)-1]]]
self.drawLine(line1[0],line1[1])
self.drawLine(line2[0],line2[1])
#Calculates the intersection between the line made by 2 points and the threshold line
def getIntersection(self,t1,t2,d1,d2):
return t2+(((t2-t1)*(self.th-d2))/(d2-d1))
def drawGreenLine(self,t_line,d_line):
self.score+=(t_line[1]-t_line[0])
plt.plot(t_line,d_line,'g-')
def drawRedLine(self,t_line,d_line):
plt.plot(t_line,d_line,'r-')
# Updates score
def update_score(self):
if self.score <= vt.delta_t.total_seconds():
self.score_text.set_text(str('Score: %.2f secs' % self.score))
else:
self.score_text.set_text('Score: ' + str(vt.delta_t.total_seconds())+ ' secs')
#Updates timer
def update_title(self):
#self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopClkTimer()
self.suptitle.set_text(
str(vt.zero_t.total_seconds()))
self.ax.figure.canvas.draw()
else:
self.suptitle.set_text(str(vt.timeLeft())[:-4])
self.ax.figure.canvas.draw()
#Updates data for drawing into the graph
#The first data belongs to 0.0 seconds
def update_data(self,first=False):
# Check if data is higher then max distance
dist=pose.update()
if first:
self.t.insert(len(self.t),0.0)
else:
self.t.insert(len(self.t),(vt.delta_t-vt.diff).total_seconds())
if dist > self.max_dist :
self.d.insert(len(self.d),self.max_dist)
else:
self.d.insert(len(self.d),dist)
# self.drawPoint(self.t[len(self.t)-1],self.d[len(self.d)-1])
if len(self.t)>=2 and len(self.d)>=2:
self.drawLine(self.t,self.d)
self.update_score()
if vt.timeLeft() <= vt.zero_t:
vt.stopDataTimer()
self.update_score()
self.ax.figure.canvas.draw()
self.fig.savefig('Result_'+str(datetime.now())+'.png', bbox_inches='tight')
#https://github.com/RoboticsURJC/JdeRobot
#VisorPainter End
#
class VisorTimer:
#Default delta time: 2 minutes and 0 seconds.
#Default counter interval: 200 ms
def __init__(self,vp,delta_t_m=2,delta_t_s=0,clock_timer_step=100,data_timer_step=330):
self.delta_t = timedelta(minutes=delta_t_m,seconds=delta_t_s)
self.zero_t = timedelta(minutes=0,seconds=0,milliseconds=0)
self.final_t = datetime.now()+self.delta_t
self.diff = self.final_t-datetime.now()
vp.setAxes(xaxis=self.delta_t.seconds)
# Creates a new clock_timer object.
self.clock_timer = vp.fig.canvas.new_timer(interval=clock_timer_step)
self.data_timer = vp.fig.canvas.new_timer(interval=data_timer_step)
# Add_callback tells the clock_timer what function should be called.
self.clock_timer.add_callback(vp.update_title)
self.data_timer.add_callback(vp.update_data)
def startTimer(self):
self.clock_timer.start()
vp.update_data(first=True)
self.data_timer.start()
def stopClkTimer(self,):
self.clock_timer.remove_callback(vp.update_title)
self.clock_timer.stop()
def stopDataTimer(self):
self.data_timer.remove_callback(vp.update_data)
self.data_timer.stop()
def timeLeft(self):
self.diff=self.final_t-datetime.now()
return self.diff
#
#VisorTimer End
#
# Main
status = 0
try:
pose = Pose(sys.argv)
pose.update()
vp = VisorPainter()
vt = VisorTimer(vp)
vp.suptitle.set_text(str(vt.delta_t))
vt.startTimer()
plt.show()
pose.finish()
except:
traceback.print_exc()
status = 1
sys.exit(status)
The result must be an image with the plt.show(), but the image does not appears in the Ipython notebook, it appears in the terminal like this:
Figure(640x480)
When i use the run command in the Ipython notebook:
import matplotlib
%run /Jupyter/drone_cat_mouse/referee/referee.py /Jupyter/drone_cat_mouse/referee/referee.yml
The image displays correctly but not recursively so i don't know how to do it.
Thanks for help.
I'm really unsure what your problem is. I wrote a script that looks like this:
#! /usr/bin/env python3
# plotter.py
import sys
import matplotlib.pyplot as plt
def main(x):
plt.plot(x)
plt.show()
if __name__ == '__main__':
main([float(v) for v in sys.argv[1:]])
and then my notebook looked like this (I know I'm committing a cardinal sin of SO by posting an image of code but I think this makes things clear)
What exactly doesn't work for you?
I have a problem with my pixel_at function in my code. Here are all necessary code snippets.
Here is my Helper module:
import gtk.gdk
def pixel_at(x, y):
gtk.gdk
rw = gtk.gdk.get_default_root_window()
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 1, 1)
pixbuf = pixbuf.get_from_drawable(rw, rw.get_colormap(), x, y, 0, 0, 1, 1)
return tuple(pixbuf.pixel_array[0, 0])
Here is the Controller class for initilaizing:
import Sensors as sensor
import Scanner as sc
import pyautogui
import time
class GameController(object):
COLOR_DINOSAUR = (83, 83, 83)
def __init__(self):
self.offset = (230, 270) #hardcoded Offset
self.width = 600 # hardcoded but alwways the same
# Stores points (jumps)
self.points = 0
#Listeners
self.onGameEnd = None
self.onGameStart = None
self.onSensorData = None
#Game State
self.gameState = 'OVER'
self.sensor = sensor.Sensors()
self.Scanner = sc.Scanner()
Here is my main function:
import pyautogui
import Helper
import GameController as GC
def main():
print Helper.pixel_at(25, 5)
Controller = GC.GameController()
# Just test values for this function
Controller.Scanner.scanUntil([75, 124],
(-2, 0),
(83, 83, 83),
75 / 2)
Here is the code for the Scanner:
import pyautogui
import Helper
def scanUntil(self, start, step, matchColor, iterLimit):
iterations = 0
current = self.makeInBounds(start)
if step[X] == 0 and step[Y] == 0:
return None
while not self.isOutOfBound(current):
color = Helper.pixel_at(current[X], current[Y])
# to slow color = pyautogui.pixel(current[X], current[Y])
if color == matchColor: # Tuple comparison
return current
current[X] += step[X]
current[Y] += step[Y]
iterations += 1
if iterations > iterLimit:
return None
return None
This line color = Helper.pixel_at(current[X], current[Y]) in the Scanner throws the error: Fatal IO error 0 (Success) on X server :0. But the print Helper.pixel_at(25, 5)call in main() returns me the correct RGB tuple.
The error is thrown at pixbuf = pixbuf.get_from_drawable(rw, rw.get_colormap(), x, y, 0, 0, 1, 1) in the pixel_at(x,y) so I think it has something to do with this.
From the documentation at http://www.pygtk.org/pygtk2reference/class-gdkpixbuf.html#method-gdkpixbuf--get-from-drawable I got this clue: In other words, copies image data from a server-side drawable to a client-side RGB(A) buffer.
But I donĀ“t understand this server-side and client side and why is it working in my first print call but not for color = Helper.pixel_at(current[X], current[Y])?
I have a python program that controls a Brookstone Rover 2.0 from a computer through openCV and pygame and I've been working on this for 7 hours now and it has been nothing but errors after errors and this is what it's showing me now
PS C:\users\sessi\desktop> C:\Users\sessi\Desktop\rover\ps3rover20.py
Traceback (most recent call last):
File "C:\Users\sessi\Desktop\rover\ps3rover20.py", line 168, in <module>
rover = PS3Rover()
File "C:\Users\sessi\Desktop\rover\ps3rover20.py", line 58, in __init__
Rover20.__init__(self)
File "C:\Program Files\Python 3.5\lib\site-packages\roverpylot-0.1-py3.5.egg\rover\__init__.py", line 182, in __init__
Rover.__init__(self)
File "C:\Program Files\Python 3.5\lib\site-packages\roverpylot-0.1-py3.5.egg\rover\__init__.py", line 47, in __init__
self._sendCommandIntRequest(0, [0, 0, 0, 0])
File "C:\Program Files\Python 3.5\lib\site-packages\roverpylot-0.1-py3.5.egg\rover\__init__.py", line 149, in _sendCommandIntRequest
bytevals.append(ord(c))
TypeError: ord() expected string of length 1, but int found
These are the two files that it's getting errors from
ps3rover20.py
#!/usr/bin/env python
'''
ps3rover20.py Drive the Brookstone Rover 2.0 via the P3 Controller, displaying
the streaming video using OpenCV.
Copyright (C) 2014 Simon D. Levy
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
# You may want to adjust these buttons for your own controller
BUTTON_LIGHTS = 3 # Square button toggles lights
BUTTON_STEALTH = 1 # Circle button toggles stealth mode
BUTTON_CAMERA_UP = 0 # Triangle button raises camera
BUTTON_CAMERA_DOWN = 2 # X button lowers camera
# Avoid button bounce by enforcing lag between button events
MIN_BUTTON_LAG_SEC = 0.5
# Avoid close-to-zero values on axis
MIN_AXIS_ABSVAL = 0.01
from rover import Rover20
import time
import pygame
import sys
import signal
# Supports CTRL-C to override threads
def _signal_handler(signal, frame):
frame.f_locals['rover'].close()
sys.exit(0)
# Try to start OpenCV for video
try:
import cv
except:
cv = None
# Rover subclass for PS3 + OpenCV
class PS3Rover(Rover20):
def __init__(self):
# Set up basics
Rover20.__init__(self)
self.wname = 'Rover 2.0: Hit ESC to quit'
self.quit = False
# Set up controller using PyGame
pygame.display.init()
pygame.joystick.init()
self.controller = pygame.joystick.Joystick(0)
self.controller.init()
# Defaults on startup: lights off, ordinary camera
self.lightsAreOn = False
self.stealthIsOn = False
# Tracks button-press times for debouncing
self.lastButtonTime = 0
# Try to create OpenCV named window
try:
if cv:
cv.NamedWindow(self.wname, cv.CV_WINDOW_AUTOSIZE )
else:
pass
except:
pass
self.pcmfile = open('rover20.pcm', 'w')
# Automagically called by Rover class
def processAudio(self, pcmsamples, timestamp_10msec):
for samp in pcmsamples:
self.pcmfile.write('%d\n' % samp)
# Automagically called by Rover class
def processVideo(self, jpegbytes, timestamp_10msec):
# Update controller events
pygame.event.pump()
# Toggle lights
self.lightsAreOn = self.checkButton(self.lightsAreOn, BUTTON_LIGHTS, self.turnLightsOn, self.turnLightsOff)
# Toggle night vision (infrared camera)
self.stealthIsOn = self.checkButton(self.stealthIsOn, BUTTON_STEALTH, self.turnStealthOn, self.turnStealthOff)
# Move camera up/down
if self.controller.get_button(BUTTON_CAMERA_UP):
self.moveCameraVertical(1)
elif self.controller.get_button(BUTTON_CAMERA_DOWN):
self.moveCameraVertical(-1)
else:
self.moveCameraVertical(0)
# Set treads based on axes
self.setTreads(self.axis(1), self.axis(3))
# Display video image if possible
try:
if cv:
# Save image to file on disk and load as OpenCV image
fname = 'tmp.jpg'
fd = open(fname, 'w')
fd.write(jpegbytes)
fd.close()
image = cv.LoadImage(fname)
# Show image
cv.ShowImage(self.wname, image )
if cv.WaitKey(1) & 0xFF == 27: # ESC
self.quit = True
else:
pass
except:
pass
# Converts Y coordinate of specified axis to +/-1 or 0
def axis(self, index):
value = -self.controller.get_axis(index)
if value > MIN_AXIS_ABSVAL:
return 1
elif value < -MIN_AXIS_ABSVAL:
return -1
else:
return 0
# Handles button bounce by waiting a specified time between button presses
def checkButton(self, flag, buttonID, onRoutine=None, offRoutine=None):
if self.controller.get_button(buttonID):
if (time.time() - self.lastButtonTime) > MIN_BUTTON_LAG_SEC:
self.lastButtonTime = time.time()
if flag:
if offRoutine:
offRoutine()
flag = False
else:
if onRoutine:
onRoutine()
flag = True
return flag
# main -----------------------------------------------------------------------------------
if __name__ == '__main__':
# Create a PS3 Rover object
rover = PS3Rover()
# Set up signal handler for CTRL-C
signal.signal(signal.SIGINT, _signal_handler)
# Loop until user hits quit button on controller
while not rover.quit:
pass
# Shut down Rover
rover.close()
__ init__.py
'''
Python classes for interacting with the Brookstone Rover 2.0
and Rover Revolution
Copyright (C) 2015 Simon D. Levy
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
import struct
import threading
import socket
import time
from blowfish import Blowfish
from adpcm import decodeADPCMToPCM
from byteutils import *
class Rover:
def __init__(self):
''' Creates a Rover object that you can communicate with.
'''
self.HOST = '192.168.1.100'
self.PORT = 80
TARGET_ID = 'AC13'
TARGET_PASSWORD = 'AC13'
self.TREAD_DELAY_SEC = 1.0
self.KEEPALIVE_PERIOD_SEC = 60
# Create command socket connection to Rover
self.commandsock = self._newSocket()
# Send login request with four arbitrary numbers
self._sendCommandIntRequest(0, [0, 0, 0, 0])
# Get login reply
reply = self._receiveCommandReply(82)
# Extract Blowfish key from camera ID in reply
cameraID = reply[25:37].decode('utf-8')
key = TARGET_ID + ':' + cameraID + '-save-private:' + TARGET_PASSWORD
# Extract Blowfish inputs from rest of reply
L1 = bytes_to_int(reply, 66)
R1 = bytes_to_int(reply, 70)
L2 = bytes_to_int(reply, 74)
R2 = bytes_to_int(reply, 78)
# Make Blowfish cipher from key
bf = _RoverBlowfish(key)
# Encrypt inputs from reply
L1,R1 = bf.encrypt(L1, R1)
L2,R2 = bf.encrypt(L2, R2)
# Send encrypted reply to Rover
self._sendCommandIntRequest(2, [L1, R1, L2, R2])
# Ignore reply from Rover
self._receiveCommandReply(26)
# Start timer task for keep-alive message every 60 seconds
self._startKeepaliveTask()
# Set up vertical camera controller
self.cameraVertical = _RoverCamera(self, 1)
# Send video-start request
self._sendCommandIntRequest(4, [1])
# Get reply from Rover
reply = self._receiveCommandReply(29)
# Create media socket connection to Rover
self.mediasock = self._newSocket()
# Send video-start request based on last four bytes of reply
self._sendRequest(self.mediasock, 'V', 0, 4, map(ord, reply[25:]))
# Send audio-start request
self._sendCommandByteRequest(8, [1])
# Ignore audio-start reply
self._receiveCommandReply(25)
# Receive images on another thread until closed
self.is_active = True
self.reader_thread = _MediaThread(self)
self.reader_thread.start()
def close(self):
''' Closes off commuincation with Rover.
'''
self.keepalive_timer.cancel()
self.is_active = False
self.commandsock.close()
if self.mediasock:
self.mediasock.close()
def turnStealthOn(self):
''' Turns on stealth mode (infrared).
'''
self._sendCameraRequest(94)
def turnStealthOff(self):
''' Turns off stealth mode (infrared).
'''
self._sendCameraRequest(95)
def moveCameraVertical(self, where):
''' Moves the camera up or down, or stops moving it. A nonzero value for the
where parameter causes the camera to move up (+) or down (-). A
zero value stops the camera from moving.
'''
self.cameraVertical.move(where)
def _startKeepaliveTask(self,):
self._sendCommandByteRequest(255)
self.keepalive_timer = \
threading.Timer(self.KEEPALIVE_PERIOD_SEC, self._startKeepaliveTask, [])
self.keepalive_timer.start()
def _sendCommandByteRequest(self, id, bytes=[]):
self._sendCommandRequest(id, len(bytes), bytes)
def _sendCommandIntRequest(self, id, intvals):
bytevals = []
for val in intvals:
for c in struct.pack('I', val):
bytevals.append(ord(c))
self._sendCommandRequest(id, 4*len(intvals), bytevals)
def _sendCommandRequest(self, id, n, contents):
self._sendRequest(self.commandsock, 'O', id, n, contents)
def _sendRequest(self, sock, c, id, n, contents):
bytes = [ord('M'), ord('O'), ord('_'), ord(c), id, \
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, n, 0, 0, 0, 0, 0, 0, 0]
bytes.extend(contents)
request = ''.join(map(chr, bytes))
sock.send(request)
def _receiveCommandReply(self, count):
reply = self.commandsock.recv(count)
return reply
def _newSocket(self):
sock = socket.socket()
sock.connect((self.HOST, self.PORT))
return sock
def _sendDeviceControlRequest(self, a, b) :
self._sendCommandByteRequest(250, [a,b])
def _sendCameraRequest(self, request):
self._sendCommandByteRequest(14, [request])
class Rover20(Rover):
def __init__(self):
Rover.__init__(self)
# Set up treads
self.leftTread = _RoverTread(self, 4)
self.rightTread = _RoverTread(self, 1)
def close(self):
''' Closes off commuincation with Rover.
'''
Rover.close(self)
# Stop moving treads
self.setTreads(0, 0)
def getBatteryPercentage(self):
''' Returns percentage of battery remaining.
'''
self._sendCommandByteRequest(251)
reply = self._receiveCommandReply(32)
return 15 * ord(reply[23])
def setTreads(self, left, right):
''' Sets the speed of the left and right treads (wheels). + = forward;
- = backward; 0 = stop. Values should be in [-1..+1].
'''
currTime = time.time()
self.leftTread.update(left)
self.rightTread.update(right)
def turnLightsOn(self):
''' Turns the headlights and taillights on.
'''
self._setLights(8)
def turnLightsOff(self):
''' Turns the headlights and taillights off.
'''
self._setLights(9)
def _setLights(self, onoff):
self._sendDeviceControlRequest(onoff, 0)
def processVideo(self, jpegbytes, timestamp_10msec):
''' Proccesses bytes from a JPEG image streamed from Rover.
Default method is a no-op; subclass and override to do something
interesting.
'''
pass
def processAudio(self, pcmsamples, timestamp_10msec):
''' Proccesses a block of 320 PCM audio samples streamed from Rover.
Audio is sampled at 8192 Hz and quantized to +/- 2^15.
Default method is a no-op; subclass and override to do something
interesting.
'''
pass
def _spinWheels(self, wheeldir, speed):
# 1: Right, forward
# 2: Right, backward
# 4: Left, forward
# 5: Left, backward
self._sendDeviceControlRequest(wheeldir, speed)
class Revolution(Rover):
def __init__(self):
Rover.__init__(self)
self.steerdir_prev = 0
self.command_prev = 0
self.goslow_prev = 0
self.using_turret = False
# Set up vertical camera controller
self.cameraHorizontal = _RoverCamera(self, 5)
def drive(self, wheeldir, steerdir, goslow):
goslow = 1 if goslow else 0
command = 0
if wheeldir == +1 and steerdir == 0:
command = 1
if wheeldir == -1 and steerdir == 0:
command = 2
if wheeldir == 0 and steerdir == +1:
command = 4
if wheeldir == 0 and steerdir == -1:
command = 5
if wheeldir == +1 and steerdir == -1:
command = 6
if wheeldir == +1 and steerdir == +1:
command = 7
if wheeldir == -1 and steerdir == -1:
command = 8
if wheeldir == -1 and steerdir == +1:
command = 9
if steerdir == 0 and self.steerdir_prev != 0:
command = 3
if command != self.command_prev or goslow != self.goslow_prev:
self._sendDeviceControlRequest(command, goslow)
self.steerdir_prev = steerdir
self.command_prev = command
self.goslow_prev = goslow
def processVideo(self, imgbytes, timestamp_msec):
''' Proccesses bytes from an image streamed from Rover.
Default method is a no-op; subclass and override to do something
interesting.
'''
pass
def processAudio(self, audiobytes, timestamp_msec):
''' Proccesses a block of 1024 PCM audio samples streamed from Rover.
Audio is sampled at 8192 Hz and quantized to +/- 2^15.
Default method is a no-op; subclass and override to do something
interesting.
'''
pass
def useTurretCamera(self):
''' Switches to turret camera.
'''
self._sendUseCameraRequest(1)
def useDrivingCamera(self):
''' Switches to driving camera.
'''
self._sendUseCameraRequest(2)
def moveCameraHorizontal(self, where):
''' Moves the camera up or down, or stops moving it. A nonzero value for the
where parameter causes the camera to move up (+) or down (-). A
zero value stops the camera from moving.
'''
self.cameraHorizontal.move(where)
def _sendUseCameraRequest(self, camera):
self._sendCommandByteRequest(19, [6, camera])
# "Private" classes ===========================================================
# A special Blowfish variant with P-arrays set to zero instead of digits of Pi
class _RoverBlowfish(Blowfish):
def __init__(self, key):
ORIG_P = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
self._keygen(key, ORIG_P)
# A thread for reading streaming media from the Rover
class _MediaThread(threading.Thread):
def __init__(self, rover):
threading.Thread.__init__(self)
self.rover = rover
self.BUFSIZE = 1024
def run(self):
# Accumulates media bytes
mediabytes = ''
# Starts True; set to False by Rover.close()
while self.rover.is_active:
# Grab bytes from rover, halting on failure
try:
buf = self.rover.mediasock.recv(self.BUFSIZE)
except:
break
# Do we have a media frame start?
k = buf.find('MO_V')
# Yes
if k >= 0:
# Already have media bytes?
if len(mediabytes) > 0:
# Yes: add to media bytes up through start of new
mediabytes += buf[0:k]
# Both video and audio messages are time-stamped in 10msec units
timestamp = bytes_to_uint(mediabytes, 23)
# Video bytes: call processing routine
if ord(mediabytes[4]) == 1:
self.rover.processVideo(mediabytes[36:], timestamp)
# Audio bytes: call processing routine
else:
audsize = bytes_to_uint(mediabytes, 36)
sampend = 40 + audsize
offset = bytes_to_short(mediabytes, sampend)
index = ord(mediabytes[sampend+2])
pcmsamples = decodeADPCMToPCM(mediabytes[40:sampend], offset, index)
self.rover.processAudio(pcmsamples, timestamp)
# Start over with new bytes
mediabytes = buf[k:]
# No media bytes yet: start with new bytes
else:
mediabytes = buf[k:]
# No: accumulate media bytes
else:
mediabytes += buf
class _RoverTread(object):
def __init__(self, rover, index):
self.rover = rover
self.index = index
self.isMoving = False
self.startTime = 0
def update(self, value):
if value == 0:
if self.isMoving:
self.rover._spinWheels(self.index, 0)
self.isMoving = False
else:
if value > 0:
wheel = self.index
else:
wheel = self.index + 1
currTime = time.time()
if (currTime - self.startTime) > self.rover.TREAD_DELAY_SEC:
self.startTime = currTime
self.rover._spinWheels(wheel, int(round(abs(value)*10)))
self.isMoving = True
class _RoverCamera(object):
def __init__(self, rover, stopcmd):
self.rover = rover
self.stopcmd = stopcmd
self.isMoving = False
def move(self, where):
if where == 0:
if self.isMoving:
self.rover._sendCameraRequest(self.stopcmd)
self.isMoving = False
elif not self.isMoving:
if where == 1:
self.rover._sendCameraRequest(self.stopcmd-1)
else:
self.rover._sendCameraRequest(self.stopcmd+1)
self.isMoving = True
I know almost nothing about python by the way.
I'm using windows 10 64 bit
At
https://github.com/simondlevy/RoverPylot
it says:
Tips for Windows
Rob Crawley has put a lot of work into getting RoverPylot to work
smoothly on Windows 10. Here are his changes:
Original: # Create a named temporary file for video stream tmpfile =
tempfile.NamedTemporaryFile()
Changed to: tmpfile = tempfile.NamedTemporaryFile(mode='w+b',
bufsize=0 , suffix='.avi', prefix='RoverRev',
dir='\Python27\RoverRev_WinPylot', delete=False) Original: # Wait a
few seconds, then being playing the tmp video file cmd = 'ffplay
-window_title Rover_Revolution -framerate %d %s' % (FRAMERATE, tmpfile.name)
Changed to: cmd = '/Python27/ffmpeg/bin/ffplay.exe -x 640 -y 480
-window_title Rover_Revolution -framerate %d %s' % (FRAMERATE, tmpfile.name) Your files paths may be different than those listed
below, so make your changes accordingly
Which means that the author uses Python 2.7. From your error reports I gather you use Python 3.5, which treats bytes differently. Install and use 2.7 instead.
I am using code in wxPython to show images.
I created a screen with 2 panels, one left and right.
In one of the panels (randomly chosen), I want do display an image for exactly 150ms.
How can I program this? I am relatively new to Python, and I don't find any clear way on the internet.
My code for now (without the 150ms):
import wxversion
wxversion.select("3.0")
import wx
import random
import timeclass Screen_1(wx.Dialog):
ri = 0
def __init__(self,parent,id,title):
wx.Dialog.__init__(self,parent,id,title,size=(400,300))
self.randomImage = random.randrange(1,3)
self.randomSlot = random.randrange(1,3)
Screen_1.ri = self.randomImage
if(self.randomSlot == 1):
self.side = 'Left'
else:
self.side = 'Right'
file = open('User.txt','a')
panel_left = wx.Panel(self,11,(-1,-1),(200,200))
self.picture_left = wx.StaticBitmap(panel_left)
font = wx.Font(13,wx.DEFAULT,wx.NORMAL,wx.BOLD)
panel_centre = wx.Panel(self,12,(200,70),(10,100))
msg = wx.StaticText(panel_centre,-1,'+',size=(10,100))
msg.SetFont(font)
panel_right = wx.Panel(self,13,(210,0),(200,200))
self.picture_right = wx.StaticBitmap(panel_right)
**self.imageName = 'im_'+str(self.randomImage)+'.png'**
if self.randomSlot == 1:
self.picture_left.SetBitmap(wx.Bitmap(self.imageName))
else:
self.picture_right.SetBitmap(wx.Bitmap(self.imageName))
wx.FutureCall(1000,self.Destroy)
self.Centre()
self.ShowModal()
def OnClick(self,event):
self.Close()
Thanks a lot!
def OnTimeUp(self,e):
#change images
self.timer.Start(15,oneShot=True) # if you want to call it again in 15 ms
def StartTimer(self):
self.timer = wx.Timer()
self.timer.Bind(wx.EVT_TIMER,self.OnTimeUp)
self.timer.Start(15,oneShot=True)
something like that ... although 15ms is very fast ...