Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Thursday, November 26, 2009

Automatic builds with some inotify magic

The inotify feature in the linux kernel allows you to receive events about changed files. Using pyinotify, here is a simple script to watch for changes to source code and run "make". Everything is hard coded right now, and since I was using CMake with out-of-source builds, the directory structure is that way. It also uses the command line notify-send tool to display notifications. Perhaps some day I will make it configurable and more useful.

Cheers.



# Notification tool
# Run with the directory to watch
# currently hardcoded to use
#
# +
# |- src - contains source code and is monitored
# |- build - assumes build to be here
#
# (c) 2009, Nikhil Marathe
# Licensed under the MIT License

import sys
import os
import subprocess
import re
from pyinotify import WatchManager, Notifier, ProcessEvent, IN_MODIFY

wm = WatchManager()

class Compile( ProcessEvent ):
def process_IN_MODIFY( self, event ):
print event.path, event.name, event.mask & IN_MODIFY

if re.match( '[a-zA-Z]*.cpp', event.name ):
os.chdir( event.path.replace( 'src', 'build' ) )
ex = subprocess.call( 'make' )
subprocess.call( ['notify-send', "Build output", "%s" % ( ex == 0 and "Success" or "Error" )] )

c = Compile()
notifier = Notifier( wm, c )
wm.add_watch( sys.argv[1], IN_MODIFY, rec=True )

notifier.loop()


Sunday, February 22, 2009

GMail Notifier on an Arduino

The exams are over and I've been hacking a bit on the Arduino today. So I came up with a simple hack which blinks an LED on the Arduino if you've got unread mails in your Gmail inbox. I assume that you're familiar with the basics of Arduino.

Equipment

  • A computer with an internet connection

  • Python

  • pySerial

  • Arduino ( I'm using Duemilanove )

  • Red LED

  • Pushbutton (optional)

  • Wires

On the computer

A python script will run continuously on the computer, and fetch the Gmail RSS feed every few minutes. pySerial will be used to notify the Arduino of new mails.

Here are our imports and constants

# ~ Gmail Notifier for Arduino
# ~ This file is released under the public domain

import httplib
import getpass
import base64
import re
import time
import serial

INTERVAL = 5 # check every INTERVAL minutes

serv = 'mail.google.com'
path = '/mail/feed/atom'
# ask user name and password and encode them for authentication

auth = base64.encodestring(
'%s:%s'%(raw_input('Username: '),
getpass.getpass()))


So first, fetching the feed. We'll use httplib. Here is the code:

def getfeed():
print 'Checking...'
conn = httplib.HTTPSConnection(serv)
conn.putrequest('GET', path)
conn.putheader('Authorization', 'Basic %s'%auth)
conn.endheaders()
return conn.getresponse().read()


Next lets get the count. Gmail replies in the following format. In the case of new mails there is more information, but we don't care about that. We're mainly interested in fullcount.

<?xml version="1.0" encoding="UTF-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#">
<title>Gmail - Inbox for nsm.nikhil@gmail.com</title>
<tagline>New messages in your Gmail Inbox</tagline>
<fullcount>0</fullcount>
<link rel="alternate" href="http://mail.google.com/mail" type="text/html" />
<modified>2009-02-22T11:00:33Z</modified>
</feed>


So we'll use regular expressions to get the count.

def count(data):
matches = re.findall('<fullcount>([0-9]+)</fullcount>', data)
if len(matches) == 0:
print 'Error in parsing feed, check user name and password are correct'
return 0
return int(matches[0])


We'll need to write this to the serial port.

def writeSer(data):
try:
# the best way to find this out is to launch the Arduino environment
# and see what it says under Tools -> Serial Port
ser = serial.Serial('/dev/ttyUSB0')
ser.write(data)
except serial.serialutil.SerialException:
print 'Error writing to serial device'
raise


Now that we're done with the functions, it's time to make them work together


# subtract so that we check first time
last_check = time.time() - INTERVAL*60

while True:
if time.time() - last_check < INTERVAL*60:
continue
last_check = time.time()
msgs = count(getfeed())
print msgs,'mails'
writeSer(str(msgs))



Thats the computer part.

On the Arduino


The circuit :



The LED is on pin 13 and goes to ground. The button takes 5V through the power pins on the analog side, via a 220 ohm resistor. The other leg is grounded. Pin 4 can be used to read the state of the button. The wire from pin 4 connects to the 5V leg of the button.

The code is dead simple and so is presented together.


int ledPin = 13; // connect led to digital pin 13, or use default small one
int bPin = 4; // connect button to digital pin 4
boolean blink = false; // holds our current state

int INTERVAL = 200; // led blink rate in milliseconds

void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
}

void loop() {
if(Serial.available() > 0)
blink = Serial.read() > 48;// 48 is 0 is ASCII

if(digitalRead(bPin) == LOW) // button pressed
blink = false;

// blink is true if we got serial input
// or we had got serial input and button hasn't been pressed yet.
if(blink) {
digitalWrite(ledPin, HIGH);
delay(INTERVAL);
digitalWrite(ledPin, LOW);
delay(INTERVAL);
}
else
digitalWrite(ledPin, LOW);
}


The button is used to switch off the blinking once you've noticed that you've got mail and don't want it to keep blinking till the next check.

Running

Verify and Upload the code to the Arduino. You can try entering numbers in the Serial Monitor to check that the circuit works. Remember to switch off the Serial Monitor.

Now start your script, python mailarduino.py. Enter authentication details. Now sit back and relax... oh wait, you've got mail.

Full python script ( mailarduino.py )

import httplib
import getpass
import base64
import re
import time
import serial

INTERVAL = 5 # check every INTERVAL minutes

serv = 'mail.google.com'
path = '/mail/feed/atom'

auth = base64.encodestring(
'%s:%s'%(raw_input('Username: '),
getpass.getpass()))

def count(data):
matches = re.findall('<fullcount>([0-9]+)</fullcount>', data)
if len(matches) == 0:
print 'Error in parsing feed, check user name and password are correct'
return 0
return int(matches[0])

def getfeed():
print 'Checking...'
conn = httplib.HTTPSConnection(serv)
conn.putrequest('GET', path)
conn.putheader('Authorization', 'Basic %s'%auth)
conn.endheaders()
return conn.getresponse().read()

def writeSer(data):
try:
ser = serial.Serial('/dev/ttyUSB0')
ser.write(data)
except serial.serialutil.SerialException:
print 'Error writing to serial device'
raise

last_check = time.time() - INTERVAL*60 # subtract so that we check first time

while True:
if time.time() - last_check < INTERVAL*60:
continue
last_check = time.time()
msgs = count(getfeed())
print msgs,'mails'
writeSer(str(msgs))

Sunday, September 14, 2008

Graffiti is almost released


Aside from some code cleanup, testing, and writing architecture documentation, Graffiti is good to go. You can Download it

Wednesday, August 27, 2008

Reworked Graffiti's first render


After quite a few weeks of work, Graffiti has (again) reached the stage where it can render it's first words. It's now at 488 lines of code, of which the CSS related stuff itself comes in at 288 lines! There is no word wrapping yet, or any layout logic. I've been busy fixing quite a few bugs in the CSS overlays and so on.

This is how you would use it.



import pygame
from pygame.locals import *

import graffiti as g # 1

pygame.init()

g.init() # 2

pygame.display.set_caption('Graffiti Render Test')

screen = pygame.display.set_mode((800, 600))
screen.fill((0, 0, 0))

page = g.page.Page('<body><p>Testing Graffiti</p></body>') # 3

page.render.on(screen) # 4

while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit(0)

pygame.display.update()


Wednesday, August 13, 2008

Custom hash objects in Python

It's quite common to use strings, integers and other 'native' Python data types as hash keys. But sometimes it is much easier to be able to use your own class instances as keys. Python's magic methods allow you to do this.

Note: This is not a tip on implementing hash functions, this is how you can remove a certain layer of peeking around into objects

__hash and __cmp__


Consider a useless HTML parser with a simple node design where you want to associate the node name with its attributes.

You want to use the absolute node name as a unique hash.

The solution is to define custom implementations for __hash__ and __cmp__, two magic methods. For more information and constraints about them take a look at the Python docs.

The builtin functions hash(obj) and cmp(obj1, obj2) will attempt to call there __underscored__ counterparts on objs.



import UserDict # allow NodeAttrs to behave like a dictionary, not significant for this example

class NodeName(object):
def __init__(self, name, parent=None):
self.name = name
self.parent = parent

def __str__(self):
return (parent and str(parent) or '') + self.name

def __hash__(self):
# the hash of our string is our unique hash
return hash(str(self))

def __cmp__(self, other):
# similarly the strings are good for comparisons
return cmp(str(self), str(other))

class NodeAttrs(UserDict.UserDict):
def __init__(self, attrs={}):
self.update(attrs)



Where we assume that the parser is doing the heavy lifting of parsing the name, and putting the attributes in a dictionary. Now to use this in a dictionary you would do the following:



>>> d = {}
>>> d[node_name] = attrs # node_name is an instance of NodeName and attrs

... # do anything which can be done to a dictionary and its keys



Thats it! For more magic methods see Python __Underscore__ Methods

Monday, August 04, 2008

Sierpinski, my first fractal



The Sierpinski triangle in Python, inspired by HTDP, and using Pygame




import sys
import pygame
from pygame.locals import *

class Point(object):
def __init__(self, x, y):
self.x, self.y = x, y

def distance_sq(self, other):
return (self.x-other.x)**2 + (self.y-other.y)**2

def distance(self, other):
return sqrt(self.distance_sq(other))

def tupl(self):
return (self.x, self.y)

def midpt(p1, p2):
return Point( (p1.x+p2.x)/2, (p1.y+p2.y)/2 )

def too_small(p1, p2, p3):
return max([ p1.distance_sq(p2), p2.distance_sq(p3), p3.distance_sq(p1) ]) < 5

def draw_line(p1, p2, surf, col):
pygame.draw.line(surf, col, p1.tupl(), p2.tupl())


def draw_triangle(a, b, c, screen, col):
if too_small(a, b, c):
return
else:
a_b = midpt(a, b)
a_c = midpt(a, c)
b_c = midpt(b, c)

draw_line(a, b, screen, col)
draw_line(b, c, screen, col)
draw_line(a, c, screen, col)

draw_triangle(a, a_b, a_c, screen, col)
draw_triangle(b, a_b, b_c, screen, col)
draw_triangle(c, b_c, a_c, screen, col)

pygame.init()
pygame.display.set_caption('Sierpinski')
screen = pygame.display.set_mode((640,480))
screen.fill((0, 0, 0))

draw_triangle(Point(10, 10), Point(630, 10), Point(310, 450), screen, (255, 0, 0))
draw_triangle(Point(50, 10), Point(630, 240), Point(310, 0), screen, (0, 255, 0))

while True:
for event in pygame.event.get():
if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
pygame.display.update()

pygame.quit()

Sunday, June 29, 2008

Graffiti

A new project of mine is Graffiti, a library over Pygame which allows application developers to write HTML and some CSS-like styling information and let Graffiti manage styling and rendering. It's no Gecko. Just something to solve an itch of handling word wrapping and styling in raw Pygame/SDL. The distinction between CSS and CSS-like is important and will be explained later. Write now I'm just on basic API design with no CSS parser, and all styles currently encoded in source. But I'll do it eventually, in a few days if there are no distractions

Friday, November 24, 2006

Glowpad


Glowpad is a small Python/Pygame program to read in a file and generate a word/pattern with randomly coloured squares which morph to white. I basically wrote it after seeing the cool effect that appears at the end of one of the Sony ads(the robot one) where the same effect happens with the word feel. So I hacked this program in just half an hour.

To run the program, extract it to any place on your computer. you NEED to have Python and Pygame installed before you can run it. After you install them just run the program by executing
python glowpad.py 
. Two patterns FEEL and LINUX are included. So to apply the effect on Linux use
python glowpad.py linux
.
To create your own patterns just create a new file in any editor. Make the pattern you want using an uppercase B. The colours are chosen randomly. You can add padding or whatever you want around the characters. The only thing Glowpad makes sense of is an uppercase B, any other character is ignored. So this is an example. Just save the file and run it as shown above

##############################
-B----BBBBB-BBBBB-B---B-B---B-
-B------B---B---B-B---B--BBB--
-B------B---B---B-B---B---B---
-B------B---B---B-B---B--BBB--
-BBBB-BBBBB-B---B-BBBBB-B---B-
##############################

DOWNLOAD:http://22bits.exofire.net/downloads/glowpad.tar.gz

Monday, October 09, 2006

LaserMania development


Lasermania finally has something to see moving. Its this game where you have to arrange specific mirrors in proper places to guide a photon from emitter to reciever. Its written in pygame/python. In the screenshot you can see the photon(red dot) to the right. The emitter now fires a photon, drag and drop works(almost), only the mirror logic is left.