Mostrando entradas con la etiqueta python. Mostrar todas las entradas
Mostrando entradas con la etiqueta python. Mostrar todas las entradas

lunes, 18 de junio de 2012

mtvcgui 1.0

Ah, cierto que tenía un blog! Más de 2 años después de la última publicación escribo para contar que actualicé después de casi 3 años sin modificaciones una aplicación que hice en PyQT y que tiene una ínfima cantidad de usuarios que cada tanto me contactan.

Se trata de una aplicación para grabar TV usando mencoder. Una GUI que permite configurar los parámetros principales para generar el comando para capturar. No es nada de otro mundo pero a veces para lograr capturar con mencoder hay que recorrer varios foros y leer la man page hasta encontrar lo que se necesita.

En este caso me contactó un italiano para agregar algunos parámetros de configuración para capturar con xvid y x264. Además, como él usa un dispositivo diferente al mío, la lista de normas soportadas es diferente, y eso venía hardcodeado. Ahora se obtiene a través de mplayer, al igual que la lista de inputs y los codecs soportados.

Aproveché el intercambio de mails para pedirle que me ayude con la traducción a italiano así que eso también está incluido.

Son varios cambios, así que pasó de la versión 0.31 a la 1.0. Pero no era mi idea original. Quería que fuera la 0.4 pero cuando quise hacer un ebuild de esto para Gentoo, vi que la 0.4 no la tomaba como una versión posterior, y claro, 31 es mayor que 4, idiota. Había hecho todo mal, tendría que haber sido la 0.3.1.

Si bien el programa es en python y bastante simple, por lo tanto anda descomprimiendo el .tgz en cualquier lado, aproveché para generar un par de paquetes para otras distros. Hice un .rpm bastante rústico y generé un .deb a partir de ese usando alien.

Si alguien con conocimientos de empaquetado se ofrece para ayudarme a hacerlo bien, se lo agradecería. Sobre todo si sabe como hacer distribuible correctamente una aplicación en Python. En este caso simplemente copié todo a /opt/mtvcgui, aunque sé que no es lo mejor. De hecho algunas guías de empaquetado decían en negrita que no había que hacerlo. Viva la anarquía (?)

La aplicación está acá: http://code.google.com/p/mtvcgui/




lunes, 22 de junio de 2009

mtvcgui 0.31

Retomé el experimento con PyQT y QTDesigner que había abandonado hace cerca de 6 meses, motivado por los mails con comentarios y sugerencias (¡y problemas!) de ¡3! personas que lo usaron. Además noté que aparece en ¡una página y un foro! :P

Corregí un par de problemas menores y le agregué la funcionalidad de ajustar brillo, contraste, matiz y saturación, y de ver esos cambios en la previsualización del canal a medida que se realizan. También, ahora se puede cambiar de canal y norma mientras se previsualiza.
Esto lo hago usando mplayer en modo esclavo (-slave). De esa manera recibe comandos desde la entrada estándar.

Por ejemplo, para seleccionar un canal, lo hago con
self.mplayer_instance.stdin.write('tv_set_channel %s\n' % str(channel))

donde mplayer_instance es un objeto creado con subprocess.Popen y el comando mplayer con sus argumentos.
En la página dice que hay que usar communicate() en lugar de stdin.write(), pero a mi no me sirvió, porque communicate se queda esperando que el proceso termine y leyendo su stdout, y no es lo que necesito hacer.
A lo mejor podría hacerlo desde un thread, pero... ¡bah!

Igual, no anda demasiado bien y no sé como solucionarlo. El video generado cada tanto se pone más lento y después se arregla solo, sin embargo no se reportan cuadros perdidos. Codifico con lavc(mpeg4) y le aplico los filtros de desentrelazado, etc, todo al vuelo. Podría ser que no da el CPU, pero no supera el 50%... Capaz que mencoder no es la mejor herramienta para capturar TV... :(
La GUI no debería consumir muchos recursos como para molestar en la codificación, salvo que esté haciendo algo muy mal (es posible). En teoría, lo único que se ejecuta mientras se captura es un timer cada 1 segundo, que hace un poll a la instancia de mencoder para ver si sigue viva.

El programa está en google code, github y kde-apps. ¿Para qué? :D

domingo, 29 de marzo de 2009

Python is my religion

Python Argentina pyar ultima cena
Python es una religión: Tremenda reconstrucción de la Última Cena por los chicos de PyAr en el último PyCamp. No los conozco a todos personalmente pero sí a la mayoría. Son co-listeros de Python Argentina. Entre ellos, compañeros de trabajo, de la facu, de fiestas nerd en Córdoba, etc. Muy muy grossa foto.

sábado, 7 de marzo de 2009

Limiting memory usage of a python script

I've been running recently some python and java experiments with important memory requirements on a machine shared with other people, some of them with the same needs on that machine.
There were no restrictions on resources usage by a user or a process, so controlled memory usage was the user's responsibility.

Java has a parameter to limit the amount of memory the virtual machine can use. If it exceeds that value, the machine exits and the running program fails. It is specified with java -Xmx value.

I couldn't find something similar for python so I procrastinated a bit writing a bash script to control the processes.
I made a cycle that monitored the values from /proc/meminfo and killed the running process if some limit was reached.
The problem with that solution was that my experiments were not just one program, but many, controlled by a bash script, so this solution would just kill the bash script and not the process doing the important memory usage. So I added some extra code borrowed from the web to kill also the subprocesses. That made the code longer and more complex, leading to this:

control_run.sh:
PROGRAM=$*

#Also kills process children, and their children, and so on
KILL_CHILDREN=1

#Minimum free memory available (in Kb.)
MEM_FREE_LIMIT=300000

#Minimum free swap (in Kb.)
SWAP_FREE_LIMIT=500000

#Run the program in background and get its PID
$PROGRAM &
PID=$!

#While the program is alive
ps $PID > /dev/null
while [ $? -eq "0" ]; do


MEM_FREE=$(grep MemFree: /proc/meminfo | egrep [0-9]+ -o)
#echo "Free Memory: ${MEM_FREE}"

SWAP_FREE=$(grep SwapFree: /proc/meminfo | egrep [0-9]+ -o)
#echo "Free Swap: ${SWAP_FREE}"

if [ "${MEM_FREE}" -le ${MEM_FREE_LIMIT} ] || [ "${SWAP_FREE}" -le ${SWAP_FREE_LIMIT} ]; then

if [ "${MEM_FREE}" -le ${MEM_FREE_LIMIT} ]; then
echo "free memory limit reached, exiting...";
else
echo "free swap limit reached, exiting...";
fi

if [ "${KILL_CHILDREN}" -eq 0 ]; then
echo "killing process with id $PID"
kill $PID
else
#code based on http://www.unix.com/unix-dummies-questions-answers/5245-script-kill-all-child-process-given-pid.html
KILL_PIDS=$PID

CHILDREN=`ps -ef| awk '$3 == '$PID' { print $2 }'`
while [ "$CHILDREN" != "" ]; do
KILL_PIDS="$KILL_PIDS $CHILDREN"
OLD_CHILDREN=$CHILDREN
CHILDREN=''
for i in $OLD_CHILDREN; do
CHILDREN="$CHILDREN `ps -ef| awk '$3 == '$i' { print $2 }'`"
done
done

echo "killing process with id $PID and its children"

for i in $KILL_PIDS
do
echo killing $i
kill $i
done

fi
exit 1;
fi

sleep 1

ps $PID > /dev/null
done

echo "program finished"


I tested it with a small python script that just consumes memory:

memory_consumer.py:
import time
a = range(100000)
while True:
a += range(100000)
print "printing something"
time.sleep(1)


and some bash scripts to execute three instances concurrently:


memory_consumer_main.sh:
#!/bin/sh
./memory_consumer_child.sh


memory_consumer_child.sh:
#!/bin/sh
python memory_consumer.py &
python memory_consumer.py &
python memory_consumer.py



we would run:
$./control_run.sh ./memory_consumer_main.sh
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
printing something
free memory limit reached, exiting...
killing process with id 9348 and its children
killing 9348
killing 9349
killing 9350
killing 9351
killing 9353



Another possibility I found was setting the memory limit with ulimit.
We can set it from the command line and enable it for the bash session, or incluide the statements in the first lines of a bash script.

That will limit the allowed memory for any process

for example:
$ ulimit -v 40000
$ ulimit -H -v 40000


There, we limited the available memory for a process to 40Mb.
Then,
$ python memory_consumer.py
printing something
printing something
printing something
printing something
Traceback (most recent call last):
File "memory_consumer.py", line 4, in
a += range(100000)
MemoryError


While simple, the problem with this approach is that it will only kill the process exceeding the memory usage. If we are running just one isolated process, that's fine. But in my case, the batch would continue in an erroneous state and produce wrong results.
Any comment or suggestion, including "hey stupid, python has the option -blabla to do exactly that" will be appreciated.

miércoles, 26 de noviembre de 2008

Experimentando con Qtdesigner y PyQt

El fin de semana estuve probando hacer algún programita con interfaz gráfica usando python y QT. Para eso usé QTDesigner para editar la UI, y PyQT4 para usar la librería QT desde python.

Seguí este tutorial: http://www.cs.usfca.edu/~afedosov/qttut/ que aunque está viejo (es para PyQT3) la idea es la misma y se lo puede adaptar con algunos ejemplos para PyQT4 que hay dando vueltas.

El resultado fue este programita para setear algunos parámetros de mencoder y utilizarlo para capturar TV.

http://code.google.com/p/mtvcgui/

miércoles, 6 de agosto de 2008

Referencias a caracteres en XML de vuelta a unicode

Los caracteres que no son son representables en algún encoding pueden representarse con su codificación en XML. Si c es un caracter, la expresión Python para generar esta representación es:
"&#%d;" % ord(c)

Cuando codificamos un texto, podemos especificar qué hacer con los caracteres que no se pueden representar en el encoding usado. Con el argumento 'ignore', simplemente no se incluyen, con 'replace' son reemplazados por un '?' y usando 'xmlcharrefreplace' obtenemos la mencionada codificación. Por ejemplo:
codificado = texto.encode(encoding, "xmlcharrefreplace")

Para recuperar la cadena original, no encontré nada haciendo una simple búsqueda, así que lo solucioné de la siguiente manera, usando una expresión regular y asumiendo que la cadena puede ser representada en el encoding por defecto (en mi caso UTF-8).

La expresión regular simplemente matchea con la forma de la representación XML, el objeto match es pasado una función que toma la parte que corresponde al número, la convierte a entero y devuelve el resultado de aplicarle unichr a ese valor. La función unichr devuelve el caracter unicode correspondiente a un número.
def unicodechar(match):
return(unichr(int(match.group(1))))

def replace_xmlrefs(string):
return re.sub("&#(\d+);",unicodechar,string)


Nada complejo, y se lo puede ver en funcionamiento:

>>> encoded = "財団 基金会"
>>> import re
>>> replace_xmlrefs(encoded)
u'\u8ca1\u56e3 \u57fa\u91d1\u4f1a'
>>> print replace_xmlrefs(encoded)
財団 基金会

La última línea, donde se imprimen los caracteres representados puede fallar si la consola no soporta esos caracteres, o no estamos usando un encoding que cubra todo unicode. Lo recomendable es usar UTF8.
Se puede iniciar el intérprete python especificando que use otro locale (vemos los disponibles con locale -a).
Por ejemplo:
LC_ALL=es_AR.utf8 python


Obviamente siempre hay gente que hace mejor las cosas, y acá encontré una forma de hacer esto que considera también los casos en que la referencia tiene un número hexadecimal.

Update 15-08-2008:
Este snippet también reemplaza las entidades HTML.

sábado, 8 de marzo de 2008

MobiGM "ported" to Python

Reciently, a friend told me about MobiGM, a simple Perl script that parses a rss feed from a gmail account, and sends notifications about new mails to an email account. Ideally, to the one corresponding to your cell phone.

He was having troubles trying to run the script. The problem was simple: The link to the code was dead, and copying and pasting the fragments of code didn't work, because some html special characters were bothering. A few replacements made the script go.

It worked fine sending mails through my ISP SMTP server, since it doesn't require authentication, but I couldn't send mails from my gmail account.
I tried modifying the script, using the module Net::SMTP::SSL, but I had no luck, so I decided to write it in Python.

It works. It has the same functionality as MobiGM and can authenticate against a SMTP server with TLS. But it uses minidom to parse the rss feed, and access to its data in a very direct way, assuming that the feed has always the correct format.
I recommend reading the original post of MobiGM from its author's blog, because it is a nice tutorial on how to build a small application to solve a problem, using various open source tools.
The program itself is not very useful, since you can do the same thing with gmail's filters. But the author remarks the posibility of customizing it, to make it react dinamically to what appears in your mailbox.


Having said that, here is the code:

#!/usr/bin/python

from xml.dom.minidom import parse

SMTP_SERVER = "smtp.yourisp.com:port" #port not needed on servers
#that listen to on port 25
#for gmail use 587,

REQUIRES_AUTHENTICATION = True
REQUIRES_TLS = True
SMTP_USERNAME = 'username'
SMTP_PASSWORD = 'password'
FROM_EMAIL = 'email@isp.com'
TO_EMAIL = 'cellphone@phonecompany.com'

def sendMail(message):
import smtplib
s = smtplib.SMTP(SMTP_SERVER)
#s.debuglevel = 5
s.ehlo()

if REQUIRES_TLS:
s.starttls()
s.ehlo()
if REQUIRES_AUTHENTICATION:
s.esmtp_features["auth"] = "LOGIN PLAIN"
s.login(SMTP_USERNAME, SMTP_PASSWORD)

s.sendmail(FROM_EMAIL, TO_EMAIL, message)
s.quit()


oldIDs = []
newIDs = []

try:
OLDID = open('ids.old', 'rw')
except:
OLDID = None
newfile = open('ids.old', 'w')
newfile.close()

if OLDID:
for line in OLDID.readlines():
oldIDs.append(line.strip())

try:
xmldoc = parse('atomfeed')
except IOError:
quit("Error opening file 'atomfeed'")
except:
quit("Problem parsing file 'atomfeed'")

entries = xmldoc.getElementsByTagName('entry')

for entry in entries:
curID = entry.getElementsByTagName('id')[0].firstChild.nodeValue
newIDs.append(curID)
if not curID in oldIDs:
curTitle = entry.getElementsByTagName('title')[0].firstChild.nodeValue
curContents = entry.getElementsByTagName('summary')[0].firstChild.nodeValue
curAuthorEmail = entry.getElementsByTagName('email')[0].firstChild.nodeValue
curAuthor = entry.getElementsByTagName('name')[0].firstChild.nodeValue
sendMail('Subject:'+ curTitle + '\n\nFrom:' + curAuthorEmail + ' [' + curAuthor + ']\n' + curContents)


OLDID = open('ids.old', 'w')

for newid in newIDs:
OLDID.write(newid + '\n')

OLDID.close()


As Perl's version, this script needs to be added to a shell script, where wget will be executed before it to receive the feed. And that script should be executed periodically by cron.
If we saved this script as mail.py, the shell script would be something like this:

#!/bin/sh
wget -O atomfeed https://mail.google.com/gmail/feed/atom --http-user=gmail.user@gmail.com --http-password=gmailPassword
./mail.py



Read the original post on MobiGM author blog for more details.