lunes, 18 de marzo de 2013

Añadiendo columnas a nautilus

Para añadir más columnas en Nautilus al visualizar en modo lista.
sudo apt-get install python-nautilus python-mutagen python-pyexiv2 python-kaa-metadata
mkdir ~/.nautilus/python-extensions
cp bsc.py ~/.nautilus/python-extensions
#!/usr/bin/python

# this script can installed to the current user account by running the following commands:

# sudo apt-get install python-nautilus python-mutagen python-pyexiv2 python-kaa-metadata
# mkdir ~/.nautilus/python-extensions
# cp bsc.py ~/.nautilus/python-extensions
# chmod a+x ~/.nautilus/python-extensions/bsc.py

# alternatively, you can be able to place the script in:
# /usr/lib/nautilus/extensions-2.0/python/

# change log:
# geb666: original bsc.py, based on work by Giacomo Bordiga
# jmdsdf: version 2 adds extra ID3 and EXIF tag support
# jmdsdf: added better error handling for ID3 tags, added mp3 length support, distinguished
#         between exif image size and true image size
# SabreWolfy: set consistent hh:mm:ss format, fixed bug with no ID3 information 
#             throwing an unhandled exception
# jmdsdf: fixed closing file handles with mpinfo (thanks gueba)
# jmdsdf: fixed closing file handles when there's an exception (thanks Pitxyoki)
# jmdsdf: added video parsing (work based on enbeto, thanks!)
# jmdsdf: added FLAC audio parsing through kaa.metadata (thanks for the idea l-x-l)
# jmdsdf: added trackno, added mkv file support (thanks ENigma885)
# jmdsdf: added date/album for flac/video (thanks eldon.t)
# jmdsdf: added wav file support thru pyexiv2
# jmdsdf: added sample rate file support thru mutagen and kaa (thanks for the idea N'ko)
# jmdsdf: fix with tracknumber for FLAC, thanks l-x-l
# draxus: support for pdf files
 
import os
import urllib
import nautilus
# for id3 support
from mutagen.easyid3 import EasyID3
from mutagen.mp3 import MPEGInfo
# for exif support
import pyexiv2
# for reading videos. for future improvement, this can also read mp3!
import kaa.metadata
# for reading image dimensions
import Image
# for reading pdf
try:
 from pyPdf import PdfFileReader
except:
 pass

class ColumnExtension(nautilus.ColumnProvider, nautilus.InfoProvider):
 def __init__(self):
  pass

 def get_columns(self):
  return (
   nautilus.Column("NautilusPython::title_column","title","Title","Song title"),
   nautilus.Column("NautilusPython::album_column","album","Album","Album"),
   nautilus.Column("NautilusPython::artist_column","artist","Artist","Artist"),
   nautilus.Column("NautilusPython::tracknumber_column","tracknumber","Track","Track number"),
   nautilus.Column("NautilusPython::genre_column","genre","Genre","Genre"),
   nautilus.Column("NautilusPython::date_column","date","Date","Date"),
   nautilus.Column("NautilusPython::bitrate_column","bitrate","Bitrate","Audio Bitrate in kilo bits per second"),
   nautilus.Column("NautilusPython::samplerate_column","samplerate","Sample rate","Sample rate in Hz"),
   nautilus.Column("NautilusPython::length_column","length","Length","Length of audio"),
   nautilus.Column("NautilusPython::exif_datetime_original_column","exif_datetime_original","EXIF Dateshot ","Get the photo capture date from EXIF data"),
   nautilus.Column("NautilusPython::exif_software_column","exif_software","EXIF Software","EXIF - software used to save image"),
   nautilus.Column("NautilusPython::exif_flash_column","exif_flash","EXIF flash","EXIF - flash mode"),
   nautilus.Column("NautilusPython::exif_pixeldimensions_column","exif_pixeldimensions","EXIF Image Size","Image size - pixel dimensions as reported by EXIF data"),
   nautilus.Column("NautilusPython::pixeldimensions_column","pixeldimensions","Image Size","Image/video size - actual pixel dimensions"),
  )

 def update_file_info(self, file):
  # set defaults to blank
  file.add_string_attribute('title', '')
  file.add_string_attribute('album', '')
  file.add_string_attribute('artist', '')
  file.add_string_attribute('tracknumber', '')
  file.add_string_attribute('genre', '')
  file.add_string_attribute('date', '')
  file.add_string_attribute('bitrate', '')
  file.add_string_attribute('samplerate', '')
  file.add_string_attribute('length', '')
  file.add_string_attribute('exif_datetime_original', '')
  file.add_string_attribute('exif_software', '')
  file.add_string_attribute('exif_flash', '')
  file.add_string_attribute('exif_pixeldimensions', '')
  file.add_string_attribute('pixeldimensions', '')

  if file.get_uri_scheme() != 'file':
   return

  # strip file:// to get absolute path
  filename = urllib.unquote(file.get_uri()[7:])
  
  # mp3 handling
  if file.is_mime_type('audio/mpeg'):
   # attempt to read ID3 tag
   try:
    audio = EasyID3(filename)
    # sometimes the audio variable will not have one of these items defined, that's why
    # there is this long try / except attempt
    try: file.add_string_attribute('title', audio["title"][0])
    except: file.add_string_attribute('title', "[n/a]")
    try: file.add_string_attribute('album', audio["album"][0])
    except: file.add_string_attribute('album', "[n/a]")
    try: file.add_string_attribute('artist', audio["artist"][0])
    except: file.add_string_attribute('artist', "[n/a]")
    try: file.add_string_attribute('tracknumber', audio["tracknumber"][0])
    except: file.add_string_attribute('tracknumber', "[n/a]")
    try: file.add_string_attribute('genre', audio["genre"][0])
    except: file.add_string_attribute('genre', "[n/a]")
    try: file.add_string_attribute('date', audio["date"][0])
    except: file.add_string_attribute('date', "[n/a]")
   except:
    # [SabreWolfy] some files have no ID3 tag and will throw this exception:
    file.add_string_attribute('title', "[no ID3]")
    file.add_string_attribute('album', "[no ID3]")
    file.add_string_attribute('artist', "[no ID3]")
    file.add_string_attribute('tracknumber', "[no ID3]")
    file.add_string_attribute('genre', "[no ID3]")
    file.add_string_attribute('date', "[no ID3]")
    
   # try to read MP3 information (bitrate, length, samplerate)
   try:
    mpfile = open (filename)
    mpinfo = MPEGInfo (mpfile)
    file.add_string_attribute('bitrate', str(mpinfo.bitrate/1000) + " Kbps")
    file.add_string_attribute('samplerate', str(mpinfo.sample_rate) + " Hz")
    # [SabreWolfy] added consistent formatting of times in format hh:mm:ss
    # [SabreWolfy[ to allow for correct column sorting by length
    mp3length = "%02i:%02i:%02i" % ((int(mpinfo.length/3600)), (int(mpinfo.length/60%60)), (int(mpinfo.length%60)))
    mpfile.close()
    file.add_string_attribute('length', mp3length)
   except:
    file.add_string_attribute('bitrate', "[n/a]")
    file.add_string_attribute('length', "[n/a]")
    file.add_string_attribute('samplerate', "[n/a]")
    try:
     mpfile.close()
    except: pass
 
  # image handling
  if file.is_mime_type('image/jpeg') or file.is_mime_type('image/png') or file.is_mime_type('image/gif') or file.is_mime_type('image/bmp'):
   # EXIF handling routines
   try:
    img = pyexiv2.Image(filename)
    img.readMetadata()
    file.add_string_attribute('exif_datetime_original',str(img['Exif.Photo.DateTimeOriginal']))
    file.add_string_attribute('exif_software',str(img['Exif.Image.Software']))
    file.add_string_attribute('exif_flash',str(img['Exif.Photo.Flash']))
    file.add_string_attribute('exif_pixeldimensions',str(img['Exif.Photo.PixelXDimension'])+'x'+str(img['Exif.Photo.PixelYDimension']))
   except:
    # no exif data?
    file.add_string_attribute('exif_datetime_original',"")
    file.add_string_attribute('exif_software',"")
    file.add_string_attribute('exif_flash',"")
    file.add_string_attribute('exif_pixeldimensions',"")
   # try read image info directly
   try:
    im = Image.open(filename)
    file.add_string_attribute('pixeldimensions',str(im.size[0])+'x'+str(im.size[1]))
   except:
    file.add_string_attribute('pixeldimensions',"[image read error]")

  # video/flac handling
  if file.is_mime_type('video/x-msvideo') | file.is_mime_type('video/mpeg') | file.is_mime_type('video/x-ms-wmv') | file.is_mime_type('video/mp4') | file.is_mime_type('audio/x-flac') | file.is_mime_type('video/x-flv') | file.is_mime_type('video/x-matroska') | file.is_mime_type('audio/x-wav'):
   try:
    info=kaa.metadata.parse(filename)
    try: file.add_string_attribute('length',"%02i:%02i:%02i" % ((int(info.length/3600)), (int(info.length/60%60)), (int(info.length%60))))
    except: file.add_string_attribute('length','[n/a]')
    try: file.add_string_attribute('pixeldimensions', str(info.video[0].width) + 'x'+ str(info.video[0].height))
    except: file.add_string_attribute('pixeldimensions','[n/a]')
    try: file.add_string_attribute('bitrate',str(round(info.audio[0].bitrate/1000)))
    except: file.add_string_attribute('bitrate','[n/a]')
    try: file.add_string_attribute('samplerate',str(int(info.audio[0].samplerate))+' Hz')
    except: file.add_string_attribute('samplerate','[n/a]')
    try: file.add_string_attribute('title', info.title)
    except: file.add_string_attribute('title', '[n/a]')
    try: file.add_string_attribute('artist', info.artist)
    except: file.add_string_attribute('artist', '[n/a]')
    try: file.add_string_attribute('genre', info.genre)
    except: file.add_string_attribute('genre', '[n/a]')
    try: file.add_string_attribute('tracknumber',info.trackno)
    except: file.add_string_attribute('tracknumber', '[n/a]')
    try: file.add_string_attribute('date',info.userdate)
    except: file.add_string_attribute('date', '[n/a]')     
    try: file.add_string_attribute('album',info.album)
    except: file.add_string_attribute('album', '[n/a]')
   except:
    file.add_string_attribute('length','error')
    file.add_string_attribute('pixeldimensions','error')
    file.add_string_attribute('bitrate','error')
    file.add_string_attribute('samplerate','error')
    file.add_string_attribute('title','error')
    file.add_string_attribute('artist','error')
    file.add_string_attribute('genre','error')
    file.add_string_attribute('track','error')
    file.add_string_attribute('date','error')
    file.add_string_attribute('album','error')
  # pdf handling
  if file.is_mime_type('application/pdf'):
   try:
    f = open(filename, "rb")
    pdf = PdfFileReader(f)
    try: file.add_string_attribute('title', pdf.getDocumentInfo().title)
    except: file.add_string_attribute('title', "[n/a]")
    try: file.add_string_attribute('artist', pdf.getDocumentInfo().author)
    except: file.add_string_attribute('artist', "[n/a]")
    f.close()
   except:
    file.add_string_attribute('title', "[no info]")
    file.add_string_attribute('artist', "[no info]")
     
  self.get_columns()


fuente: http://pastebin.com/WxspTtvL

viernes, 23 de noviembre de 2012

Ficheros de datos en paquete Python

Estructura para un proyecto de python, con algunos archivos de datos necesarios para la aplicación:
Para que todos los ficheros de datos se añadan al paquete de python és necesario indicar en el setup.py que deseamos añadir estos. Teniendo en cuenta una distribución de los directorios como la anterior, el programa setup.py quedaría de la siguiente manera:

#!/usr/bin/python

from setuptools import setup, find_packages
setup(
 name='mypkg',
 version='0.0.2',
 author='Miquel Perello Nieto',
 author_email='perello.nieto@gmail.com',
 url='perellonieto.com',
 packages = find_packages('src'),  # include all packages under src
 package_dir = {'':'src'},   # tell distutils packages are under src

 package_data = {
  # If any package contains *.txt files, include them:
  '': ['*.txt'],
  # And include any *.dat files found in the 'data' subdirectory
  # of the 'mypkg' package, also:
  'mypkg': ['data/*.dat'],
  }
    )



Resolviendo dependencias circulares en c++ con forward declaration

El problema de las dependencias circulares aparece en el momento en que dos clases distintas tienen una instancia o hacen referencia a la otra en su declaración.

Como ejemplo estas dos clases, las cuales tienen un puntero a una instancia de la otra:

class_a.h

#ifndef CLASS_A_H_
#define CLASS_A_H_

#include "class_b.h"

class ClassB; // Commenting this line compilation will fail

class ClassA
{
public:
 ClassA();

private:
 ClassB * b;
};

class_b.h

#ifndef CLASS_B_H_
#define CLASS_B_H_

#include "class_a.h"

class ClassA; // Commenting this line compilation will fail

class ClassB
{
public:
 ClassB();

private:
 ClassA * a;
};

En este caso hasta que una de las clases no haya sido totalmente interpretada por el compilador, éste no podrá interpretar la otra. Lo que hace que el compilador se queje de no conocer el objeto.

Para solucionar este problema se debe indicar al compilador en ClassB que ClassA es una clase, y lo mismo para la ClassA. A esta declaración "anticipada" se la llama "Forward declaration" (ver Wikipedia: Forward declaration).

En el código de arriba este problema está solucionado, pero si quereis reproducir el error comentar la linia que esta indicada.

Aqui teneis el resto de ficheros necesarios para hacer una prueba:

main.cpp

#include "class_a.h"
#include "class_b.h"

int main()
{
 ClassA a;
 ClassB b;

 return 0;
}

class_a.cpp

#include "class_a.h"
#include "cstdio"

ClassA::ClassA() {
 printf("Creating ClassA object\n");
}

class_b.cpp

#include "class_b.h"
#include "cstdio"

ClassB::ClassB() {
 printf("Creating ClassB object\n");
}

Para compilarlo todo:

Makefile

all: main

main: main.o class_a.o class_b.o
 g++ -o main main.o class_a.o class_b.o

main.o: main.cpp
 g++ -c main.cpp

class_a.o: class_a.cpp
 g++ -c class_a.cpp
 
class_b.o: class_b.cpp
 g++ -c class_b.cpp

clean: 
 rm -f *.o main

Ver explicación más detallada en la fuente : Stackoverflow

miércoles, 26 de septiembre de 2012

Dependencias proyecto Python


Para ver las dependencias de nuestro proyecto python, nos podemos ayudar de la erramineta snakefood, el qual nos puede crear de forma textual o en formato visual todas las dependencias de nuestro proyecto.

sudo apt-get install snakefood
 Una vez instalado se puede generar una grafica en formato ps con el siguiente comando:

sfood myproject | sfood-graph | dot -Tps > graph.ps
donde myproject es el directorio de tu proyecto.

Ejemplo de un trozo de grafica:


 fuente: http://furius.ca/snakefood/

Iconos del panel de gnome en movimiento

Ya me ha ocurrido varias veces que el panel superior de gnome; aun teniendo todos los iconos bloqueados; de vez en cuando me los desordenaba. Puede ser que fuera al cambiar la resolución de la pantalla, o al tener un monitor suplementario conectado al portatil.

Por lo que he visto buscando información, este problema fué solucionado en la ultima versión del panel, cuando este fue portado a GTK3, pero no se ha solucionado para las versiones antiguas. Así que he encontrado un script que hizo "bojo42", el qual bloquea el panel entero y lo desbloquea cuando queramos.

Lo suyo es guardar este script en algun lugar (por ejemplo en nuestro home "/home/USER/.gnome2_panel_block_down).
Darle permiso de ejecución.
Añadir en el panel de gnome un lanzador personalizado.
Poner como comando este mismo script.
Una vez hecho esto solo es cuestion de bloquear y desbloquear el panel con un simple click.



#!/bin/sh

### Config
NOTIFY=on #(on/off)
LOCK_TITLE="Panel Lock Down"
LOCK_MSG="Locking the GNOME panel"
UNLOCK_TITLE="Panel Lock Down"
UNLOCK_MSG="Unlocking the GNOME panel"
LOCK_ICON="/usr/share/icons/hicolor/48x48/apps/gdu-encrypted-lock.png"
UNLOCK_ICON="/usr/share/icons/hicolor/48x48/apps/gdu-encrypted-unlock.png"

### Dependency checking
if [ -z "$(which gconftool-2)" ]; then
 if [ -n "$(which gconftool-2)" ]; then
  zenity --warning --text "Error. No binary found for gconftool-2!" --title "Panel Lock Down"
 else
  echo "Panel Lock Down: Error. No binary found for gconftool-2!"
 fi
 exit 1
fi
if [ "$NOTIFY" = "on" ] && [ -z "$(which notify-send)" ]; then
 SCRIPT_LOCATION="$(pwd)/$(basename $0)"
 if [ -n "$(which zenity)" ]; then
  zenity --warning --text "Notifcations failed! Please install the libnotify-bin package or disable notifications in $SCRIPT_LOCATION" --title "Panel Lock Down"
 else
  echo "Panel Lock Down: Notifcations failed! Please install the libnotify-bin package or disable notifications in $SCRIPT_LOCATION"
 fi
 NOTIFY="off"
fi

### Main
if [ "$(gconftool-2 -g /apps/panel/global/locked_down)" = "true" ]; then
 [ "$NOTIFY" = "on" ] && notify-send -i $UNLOCK_ICON "$UNLOCK_TITLE" "$UNLOCK_MSG"
 gconftool-2 -s /apps/panel/global/locked_down --type=bool false
elif [ "$(gconftool-2 -g /apps/panel/global/locked_down)" = "false" ]; then
 [ "$NOTIFY" = "on" ] && notify-send -i $LOCK_ICON "$LOCK_TITLE" "$LOCK_MSG"
 gconftool-2 -s /apps/panel/global/locked_down --type=bool true
else
 if [ -n "$(which zenity)" ]; then
  zenity --warning --text "Error. Undefined state of global panel lock down!" --title "Panel Lock Down"
 else
  echo 'Panel Lock Down: Error. Undefined state of global panel lock down!'
 fi
 exit 1
fi



lunes, 27 de agosto de 2012

Pomorizer


Cuando uno trabaja delante de un ordenador, se hace difícil controlar el tiempo que lleva uno trabajando, y el tiempo que realmente no ha sido productivo por haber mirado e-mails o leyendo alguna noticia interesante. Y más aun si trabajas en casa, y te puedes distribuir el tiempo como te vaya mejor.
Por esa razón, hace ya tiempo que me había planteado usar algun tipo de programa que me permitiera con un par de botones controlar este tiempo, y de este modo me puse a buscar algo de información. Buscando encontré una técnica de administrar el tiempo de trabajo (o estudio) llamado Técnica Pomodoro ( "The Pomodoros Technique" ), la cual recomienda plantear-se trabajar 25 minutos seguidos sin ninguna distracción, y todo seguido tomarse 5 minutos de descanso. Esto, a demás de servir para descansar y mirar algo que te guste, sirve para despejar la mente, ya que en ocasiones se cree que se esta haciendo algo bien pero realmente se debería hacer de otro modo. Este tipo de descansos pueden ayudarte a no cometer algunos de estos errores.
Así que me he puesto a buscar algún programa sencillo con el objetivo que he comentado, y me he encontrado con algunas aplicaciones, que no me han convencido. Incluso para Android, las cuales me han parecido interesantes, pero la verdad es que teniendo el ordenador, prefiero verlo en la pantalla con un simple click.
Pues bueno, al final he decidido programarme mi propio Pomorizer, con QT y Python (ya que los estoy usando para el trabajo, y ahora mismo lo tengo fresco).
La aplicación es sencillamente un campo para introducir la cuenta atras (por ejemplo 25 minutos), un botón para empezar la cuenta atrás, o pausar-la. Un botón para resetearlo todo. Un display con el tiempo restante, una barra que va aumentando al acercarse al cero. Y al final unas pequeñas casillas para marcar cuantas veces llevamos realizando la cuenta atrás de 25 minutos (se que se puede hacer de otro modo, pero el hecho de hacer "tic" con el ratón a una casilla más me reconforta).
Bueno, dejo unas imágenes del programa, y el código ya veré si lo pongo si tengo un momento, o si por casualidad lo pidiera alguien (xDDD, algo que no creo que ocurra, ya que este Blog lo uso para apuntar mis problemas y soluciones).

jueves, 23 de agosto de 2012

Recuperando servidor con S.O. Slax


Slax es un sistema operativo linux que nos permite reparar errores en disco.
Para instalar en un pendrive :
http://www.slax.org/get_slax.php
seleccionar el .tar
Se descomprimer directamente en un USB formateado en FAT.
Se deja en la raiz del USB las carpetas /boot i /slax
Se accede mediante terminal a /boot i con permisos de root se ejecuta bootinst.sh
sudo ./bootinst.sh
Deberia aparecer un menu de instalacion advirtiendo que el USB va a quedar solo con el sistema operativo Slax.
Una vez instalado desmontar el USB y esta listo ser arrancado