Personal Website

My Web: MindEchoes.com

Friday, April 30, 2010

Ubuntu: Iconos en los Menus

Para los que les gusten los Iconos en los menús tanto del sistema como en los programas (yo por ejemplo prefiero que tengan iconos), por ahí se hayan llevado una sorpresa al instalar Ubuntu 10.04 y ver que ningún menú de ninguna aplicación tenia iconos... pero a no preocuparse!
Según leo, es una decisión que tomaron los de Gnome y se integro con la nueva versión de Ubuntu de tener por defecto deshabilitada la opción de iconos en los menús, pero TODO es configurable, así que para volver a ver los iconos solo hay que hacer lo siguiente:



Y listo!

Ubuntu 10.04 Ya Instalado!

Después de tanto tiempo esperando y escuchando las distintas criticas y comentarios que se armaron sobre la interfaz de Ubuntu, desde mi opinión quedo muy buena, muy cuidados los detalles... todavía sigo instalando programas, pero por ahora todo de 10!

Recién Instalado:


Despues de Tunearlo un poco:


Monday, April 26, 2010

Google Calendar API

Haciendo un Plugin (que todavía no esta terminado) para PyTv, me quise poner a usar la API de Google para Calendar, y aunque tiene varios ejemplos, ahí determinadas cosas para cuando se quiere trabajar con un Calendario que no sea el default que me costo encontrar, así que hago este Post:

La instalación de la librería se puede consultar en: Installing the Google Data Library

Los pasos a seguir van a ser los siguientes:
  1. Agregar los módulos necesarios
  2. Conectarse a Calendar con una cuenta de usuario.
  3. Crear un Nuevo Calendario.
  4. Mostrar los distintos Calendarios del Usuario y tomar el ID de uno especifico.
  5. Crear eventos en ese Calendario especifico
  6. Listar la información de todos los eventos entre determinadas fechas del Calendario creado.
1) Primero veamos cuales son los módulos que vamos a necesitar para el ejemplo:

import time

import gdata.calendar.service
import gdata.service
import atom.service
import gdata.calendar

2) Ahora creamos una Clase "MyCalendar" y realizamos la conexión con Calendar en el constructor:

class MyCalendar(object):

    def __init__(self):
        calendar_service = gdata.calendar.service.CalendarService()
        calendar_service.email = 'diego.sarmentero@gmail.com'
        calendar_service.password = 'PASSWORD'
        calendar_service.ProgrammaticLogin()

        self.execute(calendar_service)

Hay distintas formas de realizar la conexión como puede verse en la documentación, esta es una de esas.

3) Creando un Nuevo Calendario:

def create_calendar(self, calendar_service):
    #Crear Calendario
    calendar = gdata.calendar.CalendarListEntry()
    #Configurar Titulo del Calendario
    calendar.title = atom.Title(text='PyTv Calendar')
    #Configurar Descripcion del Calendario
    calendar.summary = atom.Summary(text='PyTv: Tv Series ' \
                              'notification utility')
    #Configurar el Color con el que se mostraran los eventos
    calendar.color = gdata.calendar.Color(value='#2952A3')
    #Especificar si se podra ver en la lista de "Mis Calendarios"
    calendar.hidden = gdata.calendar.Hidden(value='false')

    #Completar la Creación del Calendario
    new_calendar = calendar_service.InsertCalendar(
                    new_calendar=calendar)

4) Mostrar los distintos Calendarios del Usuario y tomar el ID de uno:
El ID que vamos a tomar es el del Calendario que acabamos de crear.

def print_own_calendars(self, calendar_service):
    #Obtenemos un XML con los datos de los Calendarios
    feed = calendar_service.GetOwnCalendarsFeed()
    print feed.title.text
    #Recorremos los resultados obtenidos imprimiendo
    #el número de orden y el Titulo del Calendario
    for i, a_calendar in enumerate(feed.entry):
        print '\t%s. %s' % (i, a_calendar.title.text,)
        #Si el Titulo es igual al que creamos guardamos el ID
        if a_calendar.title.text == 'PyTv Calendar':
            self.id = a_calendar.id.text
            self.id = self.id.split('full/')[1].replace('%40', '@')

5) Crear Eventos en un Calendario Especifico:
En este caso el "Calendario especifico" será aquel del cual obtuvimos el ID, pero de la misma forma que se obtuvo el ID del Calendario "PyTv Calendar", se podría haber tomado el de otro cualquiera.
Otra aclaración importante es que en este caso vamos a crear "eventos simples", pero si quisiéramos también podríamos crear eventos recurrentes como se puede consultar en la documentación.

def insert_single_event(self, calendar_service, 
                title='Unnamed Event', content='', 
                start_time=None, end_time=None, minutes=10):
    #Crear evento
    event = gdata.calendar.CalendarEventEntry()
    #Asignar titulo del Evento
    event.title = atom.Title(text=title)
    #Asignar descripción
    event.content = atom.Content(text=content)

    #Configurar Tiempo de Inicio y de Fin del Evento
    if start_time is None:
        #Si start_time es None se toma la hora actual
        #como comienzo y una hora despues para fin
        start_time = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', 
                                time.gmtime())
        end_time = time.strftime('%Y-%m-%dT%H:%M:%S.000Z', 
                                time.gmtime(time.time() + 3600))
    event.when.append(gdata.calendar.When(start_time=start_time, 
                                end_time=end_time))

    #Guardamos el Evento en el Nuevo Calendario
    #creado pasandole su ID con: self.id
    new_event = calendar_service.InsertEvent(event, 
                    '/calendar/feeds/' + self.id + '/private/full')
    return new_event

6) Listar los Eventos entre determinadas Fechas del Calendario con el que se viene trabajando

def date_range_query(self, calendar_service, 
            start_date='2010-04-24', end_date='2010-04-29'):
    print 'Events from %s to %s:' % (start_date, end_date,)
    #Colocando self.id se toma el Nuevo Calendario creado
    #colocando 'default' toma el Calendario por defecto
    query = gdata.calendar.service.CalendarEventQuery(
                self.id, 'private', 'full')
    query.start_min = start_date
    query.start_max = end_date 
    feed = calendar_service.CalendarQuery(query)
    for i, an_event in enumerate(feed.entry):
        print '\t%s. %s' % (i, an_event.title.text,)
        for a_when in an_event.when:
            print '\t\tStart time: %s' % (a_when.start_time,)
            print '\t\tEnd time:   %s' % (a_when.end_time,)


Ahora creamos una función "execute" que se llama en el "main" para que ejecute todas las operaciones que hemos creado:

def execute(self, calendar_service):
    #Crear Calendario
    self.create_calendar(calendar_service)
    #Mostrar los Calendarios y tomar ID
    self.print_own_calendars(calendar_service)
    #Crear Eventos
    #Evento simple con hora de inicio y fin
    self.insert_single_event(calendar_service, 
            title='Evento Ejemplo', content='Nada que agregar...', 
            minutes=25)
    #Evento de todo el dia
    self.insert_single_event(calendar_service, 
            title='Evento de Todo el Dia',
            content='Mirar Series :P',
            start_time='2010-04-27',
            end_time='2010-04-28')
    #Mostrar Eventos
    self.date_range_query(calendar_service)

Y como resultado en Calendar obtenemos:



Todavia no pude encontrar como hacer que al Crear el Calendario, se le pueda decir con que tipo de Notificaciones queremos que se cree, por defecto se crea sin notificaciones, de lo contrario, al "Agregar un Evento", antes de ejecutar "InsertEvent(...)", podríamos realizar lo siguiente para configurar las notificaciones:

for a_when in event.when:
    if len(a_when.reminder) > 0:
        a_when.reminder[0].minutes = minutes
    else:
        a_when.reminder.append(
            gdata.calendar.Reminder(minutes=minutes))

Y de esa forma el Evento creado emitiría una notificación (popup, mail, sms, etc) tantos minutos antes del evento.

Programming Challenges: The Programming Contest Training Manual (5)

Graphical editors such as Photoshop allow us to alter bit-mapped images in the same way that text editors allow us to modify documents. Images are represented as an M ×N array of pixels, where each pixel has a given color.
Your task is to write a program which simulates a simple interactive graphical editor.

Input
The input consists of a sequence of editor commands, one per line. Each command is represented by one capital letter placed as the first character of the line. If the command needs parameters, they will be given on the same line separated by spaces.
Pixel coordinates are represented by two integers, a column number between 1 . . . M and a row number between 1 . . . N , where 1 ≤ M, N ≤ 250. The origin sits in the upper-left corner of the table. Colors are specified by capital letters.

The editor accepts the following commands:

  • I M N: Create a new M × N image with all pixels initially colored white (O).
  • C: Clear the table by setting all pixels white (O). The size remains unchanged.
  • L X Y C: Colors the pixel (X, Y ) in color (C).
  • V X Y1 Y2 C: Draw a vertical segment of color (C) in column X, between the rows Y 1 and Y 2 inclusive.
  • H X1 X2 Y C: Draw a horizontal segment of color (C) in the row Y, between the columns X1 and X2 inclusive.
  • K X1 Y1 X2 Y2 C: Draw a filled rectangle of color C, where (X1, Y 1) is the upper-left and (X2, Y 2) the lower right corner.
  • F X Y C: Fill the region R with the color C, where R is defined as follows. Pixel (X, Y ) belongs to R. Any other pixel which is the same color as pixel (X, Y ) and shares a common side with any pixel in R also belongs to this region.
  • S Name: Write the file name in MSDOS 8.3 format followed by the contents of the current image.
  • X: Terminate the session.
Output
On every command S NAME, print the filename N AM E and contents of the current image. Each row is represented by the color contents of each pixel. See the sample output.
Ignore the entire line of any command defined by a character other than I, C, L, V, H, K, F, S, or X, and pass on to the next command. In case of other errors, the program behavior is unpredictable.

Sample Input
I 5 6
L 2 3 A
S one.bmp
G 2 3 J
F 3 3 J
V 2 3 4 W
H 3 4 2 Z
S two.bmp
X

Sample Output
one.bmp
OOOOO
OOOOO
OAOOO
OOOOO
OOOOO
OOOOO
two.bmp
JJJJJ
JJZZJ
JWJJJ
JWJJJ
JJJJJ
JJJJJ

Código Solución:
import re

class Editor(object):

def __init__(self):
self.operators = {'I':self.create_new, 'C':self.clear,
'L':self.colors, 'V':self.draw_vertical,
'H':self.draw_horizontal, 'K':self.draw_rectangle,
'F':self.fill_region, 'S':self.write_filename}

pat = re.compile('^I \d+ \d+\s|^C\s|^L \d+ \d+\s|' \
'^V \d+ \d+ \d+\s|^H \d+ \d+\s|' \
'^K \d+ \d+ \d+ \d+\s|^F \d+ \d+\s|^S .+')
patCreate = re.compile('^I \d+ \d+\s')
patTerminate = re.compile('^X\s')

f = open('input165', 'r')
input = f.readlines()
self.image = None
for com in input:
if self.image is not None and pat.match(com):
self.operators[com[0:1]](com.replace('\n', ''))
elif self.image is None and patCreate.match(com):
self.operators[com[0:1]](com.replace('\n', ''))
elif patTerminate.match(com):
break

def create_new(self, command):
self.image = []
data = command.split(' ')
m = int(data[1])
n = int(data[2])
if m > 0 and 0 < n < 251:
for i in range(n):
row = '0' * m
self.image.append(row)

def clear(self, command):
n = len(self.image)
m = len(self.image[0])
if m > 0 and 0 < n < 251:
command = 'I ' + str(m) + ' ' + str(n)
self.create_new(command)

def colors(self, command):
data = command.split(' ')
x = int(data[1]) - 1
y = int(data[2]) - 1
c = data[3]
if 0 < x < len(self.image[0]) and 0 < y < len(self.image):
self.image[y] = self.image[y][0:x] + c \
+ self.image[y][x+1:]

def draw_vertical(self, command):
data = command.split(' ')
x = int(data[1]) - 1
y = int(data[2]) - 1
y2 = int(data[3])
c = data[4]
if 0 < x < len(self.image[0]) and 0 < y < len(self.image) \
and y < y2 <= len(self.image):
for i in range(y2-y):
self.image[y+i] = self.image[y+i][0:x] + c \
+ self.image[y+i][x+1:]

def draw_horizontal(self, command):
data = command.split(' ')
x = int(data[1]) - 1
x2 = int(data[2])
y = int(data[3]) - 1
c = data[4]
cant = x2 - x
if 0 < x < len(self.image[0]) and x < x2 < \
len(self.image[0]) and 0 < y < len(self.image):
self.image[y] = self.image[y][0:x] + c * cant \
+ self.image[y][x2:]

def draw_rectangle(self, command):
data = command.split(' ')
x = int(data[1]) - 1
y = int(data[2]) - 1
x2 = int(data[3])
y2 = int(data[4])
c = data[4]
if 0 < x < len(self.image[0]) and x < x2 < \
len(self.image[0]) and 0 < y < len(self.image) \
and y < y2 <= len(self.image):
for i in range(y2-y):
command = 'H ' + str(x) + ' ' + \
str(x2) + ' ' + str(y+i)
self.draw_horizontal(command)

def fill_region(self, command):
data = command.split(' ')
x = int(data[1]) - 1
y = int(data[2]) - 1
c = data[3]
z = self.image[y][x:x+1]
self.radial(x, y, z, c)

def radial(self, x, y, z, c):
h = [x - 1 + val for val in range(3) \
if -1 < x - 1 + val < len(self.image[0])]
v = [y - 1 + val for val in range(3) \
if -1 < y - 1 + val < len(self.image)]
rang = [[corX, corY] for corX in h for corY in v \
if self.image[corY][corX:corX+1] == z]
for r in rang:
self.image[r[1]] = self.image[r[1]][0:r[0]] + c \
+ self.image[r[1]][r[0]+1:]
self.radial(r[0], r[1], z, c)

def write_filename(self, command):
data = command.split(' ')
name = data[1]
print name
for line in self.image:
print line


def main():
editor = Editor()

if __name__ == '__main__':
main()

Saturday, April 24, 2010

Nada Serio...

Este si que es un post para nada serio jeje
Pero en mi armario encontré unos juguetes de la infancia (uno de esos los tengo desde los 4 años me parece... ahora tengo 24 y viviendo a 3000 km de mi anterior casa jeje) y se merecían una foto!

Programming Challenges: The Programming Contest Training Manual (4)

Cualquier bug encontrado o mejora que se pueda hacer al código, se agradecen los comentarios!

Problema 1.6.4: LCD Display
   A friend of yours has just bought a new computer. Before this, the most powerful
machine he ever used was a pocket calculator. He is a little disappointed because he
liked the LCD display of his calculator more than the screen on his new computer! To
make him happy, write a program that prints numbers in LCD display style.

Input
The input file contains several lines, one for each number to be displayed. Each line
contains integers s and n, where n is the number to be displayed (0 ≤ n ≤ 99, 999, 999)
and s is the size in which it shall be displayed (1 ≤ s ≤ 10). The input will be terminated
by a line containing two zeros, which should not be processed.

Output
Print the numbers specified in the input file in an LCD display-style using s “-” signs
for the horizontal segments and s “|” signs for the vertical ones. Each digit occupies
exactly s + 2 columns and 2s + 3 rows. Be sure to fill all the white space occupied by
the digits with blanks, including the last digit. There must be exactly one column of
blanks between two digits.
  Output a blank line after each number. You will find an example of each digit in the
sample output below.

Sample Input
2 12345
3 67890
0 0

Sample Output

Solución;
n1 = ['   ', ' | ', '   ', ' | ', '   ']
n2 = [' - ', ' .|', ' - ', '|. ', ' - ']
n3 = [' - ', ' .|', ' - ', ' .|', ' - ']
n4 = [' . ', '|.|', ' - ', ' .|', ' . ']
n5 = [' - ', '|. ', ' - ', ' .|', ' - ']
n6 = [' - ', '|. ', ' - ', '|.|', ' - ']
n7 = [' - ', ' .|', ' . ', ' .|', ' . ']
n8 = [' - ', '|.|', ' - ', '|.|', ' - ']
n9 = [' - ', '|.|', ' - ', ' .|', ' - ']
n0 = [' - ', '|.|', ' . ', '|.|', ' - ']

dictN = {'1':n1, '2':n2, '3':n3, '4':n4, '5':n5, 
        '6':n6, '7':n7, '8':n8, '9':n9, '0':n0}

def main():
    f = open('input164', 'r')
    input = f.readlines()
    for line in input:
        nums = line.replace('\n', '').split(' ')
        create_numbers(int(nums[0]), nums[1])
        
def create_numbers(s, num):
    if s > 0:
        numbers = ''
        for i in range(5):
            line = ' '.join([dictN[n][i] for n in num]) + '\n'
            line = line.replace('.', ' '*s)
            line = line.replace('-', '-'*s)
            if i == 1 or i == 3:
                line = line * s
            numbers += line
        print numbers


if __name__ == '__main__':
    main()

Programming Challenges: The Programming Contest Training Manual (3)

Cualquier bug encontrado o mejora que se pueda hacer al código, se agradecen los comentarios!

Problema 1.6.3: The Trip
  A group of students are members of a club that travels annually to different lo-
cations. Their destinations in the past have included Indianapolis, Phoenix, Nashville,
Philadelphia, San Jose, and Atlanta. This spring they are planning a trip to Eindhoven.
  The group agrees in advance to share expenses equally, but it is not practical to share
every expense as it occurs. Thus individuals in the group pay for particular things, such
as meals, hotels, taxi rides, and plane tickets. After the trip, each student’s expenses
are tallied and money is exchanged so that the net cost to each is the same, to within
one cent. In the past, this money exchange has been tedious and time consuming. Your
job is to compute, from a list of expenses, the minimum amount of money that must
change hands in order to equalize (within one cent) all the students’ costs.

Input
Standard input will contain the information for several trips. Each trip consists of a
line containing a positive integer n denoting the number of students on the trip. This is
followed by n lines of input, each containing the amount spent by a student in dollars
and cents. There are no more than 1000 students and no student spent more than
$10,000.00. A single line containing 0 follows the information for the last trip.

Output
For each trip, output a line stating the total amount of money, in dollars and cents,
that must be exchanged to equalize the students’ costs.

Sample Input
3
10.00
20.00
30.00
4
15.00
15.01
3.00
3.01
0

Sample Output
$10.00
$11.99

Solución:
def main():
    f = open('input163', 'r')
    input = f.readlines()
    pos = 0
    cant = int(input[pos].replace('\n', ''))
    while cant != 0:
        sum = 0
        l = []
        for i in range(cant):
            sum += float(input[pos + i + 1].replace('\n', ''))
            l.append(float(input[pos + i + 1].replace('\n', '')))
        total = sum / cant
        pos += cant + 1
        minimum = 0
        for a in l:
            if (a - total) > 0:
                minimum += a-total
        print '$' + str(minimum)
        #Next group
        cant = int(input[pos].replace('\n', ''))
        
    
    
if __name__ == '__main__':
    main()
a

Friday, April 23, 2010

Programming Challenges: The Programming Contest Training Manual (2)

Cualquier bug encontrado o mejora que se pueda hacer al código, se agradecen los comentarios!

Problema 1.6.2: Minesweeper
   Have you ever played Minesweeper? This cute little game comes with a certain op-
erating system whose name we can’t remember. The goal of the game is to find where
all the mines are located within a M × N field.
   The game shows a number in a square which tells you how many mines there are
adjacent to that square. Each square has at most eight adjacent squares. The 4 × 4 field
on the left contains two mines, each represented by a “*” character. If we represent the
same field by the hint numbers described above, we end up with the field on the right:
                           *...             *100
                           ....             2210
                           .*..             1*10
                           ....             1110

Input
The input will consist of an arbitrary number of fields. The first line of each field
contains two integers n and m (0 < n, m ≤ 100) which stand for the number of lines
and columns of the field, respectively. Each of the next n lines contains exactly m
characters, representing the field.
  Safe squares are denoted by “.” and mine squares by “*,” both without the quotes.
The first field line where n = m = 0 represents the end of input and should not be
processed.

Output
For each field, print the message Field #x: on a line alone, where x stands for the
number of the field starting from 1. The next n lines should contain the field with the
“.” characters replaced by the number of mines adjacent to that square. There must
be an empty line between field outputs.

Sample Input
4 4
*...
....
.*..
....
3 5
**...
.....
.*...
0 0

Sample Output
Field #1:
*100
2210
1*10
1110

Field #2:
**100
33200
1*100

Solución:
import re

def main():
    f = open('input162', 'r')
    input = f.readlines()
    content = ''.join(line for line in input)
    pat = re.compile('(\d)')
    content = [c for c in pat.split(content) if c != '' and c != ' ']
    for i in range(len(content)/3):
        pos = i*3
        height = int(content[pos])
        width = int(content[pos+1])
        mines = content[pos+2]
        result = [m.replace('.', '0') for m in 
                    mines.split('\n') if m != '']
        resolve(height, width, result)
        if height > 0 and width > 0:
            print 'Field #' + str(i+1)
            for r in result:
                print r
            print '\n'
        
def resolve(height, width, result):
    mines = [[i, j] for i in range(height) 
        for j in range(width) if result[i][j] == '*']
    for m in mines:
        y = m[0]
        x = m[1]
        x -= 1
        sumar(result, x, y, height, width)
        y -= 1
        sumar(result, x, y, height, width)
        x += 1
        sumar(result, x, y, height, width)
        x += 1
        sumar(result, x, y, height, width)
        y += 1
        sumar(result, x, y, height, width)
        y += 1
        sumar(result, x, y, height, width)
        x -= 1
        sumar(result, x, y, height, width)
        x -= 1
        sumar(result, x, y, height, width)
        
def sumar(result, x, y, h, w):
    if -1 < x < w and -1 < y < h and result[y][x] != '*':
        result[y] = result[y][:x] + \
                str(int(result[y][x]) + 1) + result[y][x+1:]
        
    
if __name__ == '__main__':
    main()

Programming Challenges: The Programming Contest Training Manual

Voy a ir subiendo los ejercicios que vaya resolviendo de "Programming Challenges", los enunciados los voy a poner en ingles como están en el libro para evitar cualquier tipo de problemas en la traducción.
Cualquier bug encontrado o mejora que se pueda hacer al código, se agradecen los comentarios!

Problema 1.6.1: The 3n + 1 Problem

   Consider the following algorithm to generate a sequence of numbers. Start with an
integer n. If n is even, divide by 2. If n is odd, multiply by 3 and add 1. Repeat this
process with the new value of n, terminating when n = 1. For example, the following
sequence of numbers will be generated for n = 22:
                        22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
It is conjectured (but not yet proven) that this algorithm will terminate at n = 1 for
every integer n. Still, the conjecture holds for all integers up to at least 1, 000, 000.
   For an input n, the cycle-length of n is the number of numbers generated up to and
including the 1. In the example above, the cycle length of 22 is 16. Given any two
numbers i and j, you are to determine the maximum cycle length over all numbers
between i and j, including both endpoints.

Input
The input will consist of a series of pairs of integers i and j, one pair of integers per
line. All integers will be less than 1,000,000 and greater than 0.

Output
For each pair of input integers i and j, output i, j in the same order in which they
appeared in the input and then the maximum cycle length for integers between and
including i and j. These three numbers should be separated by one space, with all three
numbers on one line and with one line of output for each line of input.

Sample Input
1 10
100 200
201 210
900 1000

Sample Output
1 10 20
100 200 125
201 210 89
900 1000 174

Solución:
def main():
    f = open('input161', 'r')
    input = f.readlines()
    for line in input:
        n, n2 = line.replace('\n', '').split(' ')
        n, n2 = int(n), int(n2)
        maxCycle = 0
        for i in range((n2-n) + 1):
            l = []
            num = n + i
            l.append(num)
            while num != 1:
                num = num/2 if num % 2 == 0 else num*3+1
                l.append(num)
            if len(l) > maxCycle:
                maxCycle = len(l)
        print n, n2, maxCycle

if __name__ == '__main__':
    main()

Nueva Dirección

Hace tiempo que esta funcionando, pero nunca lo puse en el blog y lo tendría que haber posteado...
Ahora para acceder al blog se puede poner tambien:

o

Sigue siendo esta misma página, cualquiera de las url anteriores lo que hacen es redireccionar a: http://utopia555.blogspot.com... pero las otras son mas fáciles de recordar :P

Chiste Informatico

Este chiste lo escuche hoy en clase:

¿Qué hace un Ingeniero en Sistemas si se le descompone el auto?
- Sale y vuelve a entrar!

jejeje a mi me causo gracia!

Thursday, April 22, 2010

Como Crear Plugins para PyTv

Ya que andamos con el Release 2.0 de PyTv, versión que ahora soporta Plugins, vamos a explicar como escribir un Plugin para PyTv.

Para empezar, es necesario que el Plugin a crear extienda de la clase "pluginbase" de PyTv y que nuestro Plugin tenga una Clase de nombre "Plugin" que es con la cual PyTv instanciara el Plugin.

import pluginbase
class Plugin(pluginbase.PluginBase):

El Plugin que se esta creando, hereda una serie de configuraciones de PluginBase que pueden sobreescribirse dependiendo de lo que sea necesario para la funcionalidades que se quieran implementar.
Estas configuraciones son:

  • _name: (String) especifica el nombre del Plugin
  • _description: (String) ofrece una descripción de cual es la funcionalidad del Plugin.
  • _icon: (String) especifica a través de una ruta relativa cual es el icono asociado con este Plugin.
  • _toThread: (Boolean) especifica si el Plugin sera incluido en el hilo principal de PyTv, de esta forma si el Plugin hace uso de notificaciones cada cierto tiempo, no sera necesario que genere ningún hilo, sino que cada vez que se ejecute el hilo de PyTv se llamara al mismo método del Plugin.
  • _toShow: (Boolean) especifica si este Plugin posee una interfaz gráfica con la que el usuario podrá interactuar.
  • _authors: (List-of-Strings) contiene los nombres de los autores del Plugin.
Ejemplo:

class Plugin(pluginbase.PluginBase):
    _name = 'Example Plugin'
    _description = 'This plugin is just for example purpose'
    _icon = 'example/Goomba.png'
    _toThread = True
    _toShow = True
    _authors = ['Diego Sarmentero <diego.sarmentero@gmail.com>']

Métodos de PluginBase:
Dependiendo en la configuración especificada para el Plugin en base a la funcionalidad que se quiera brindar, sera necesario implementar alguno o todos de los 3 métodos que son heredados de PluginBase:

configure(self, db=None, notif=None, dirPlugins=None)
Este método es ejecutado al instanciar el Plugin y en cada ciclo del hilo de PyTv (si dicho Plugin es agregado a este hilo), y este método recibe 3 objetos:
  • db: Instancia de la Clase TvDB para poder interactuar con la Base de Datos y recuperar información de las series, etc.
  • notif: Instancia de la Clase Notificator para poder emitir notificaciones usando el sistema de notificaciones de PyTv.
  • dirPlugins: String especificando la ruta absoluta donde se encuentra el directorio de los Plugins de PyTv para poder armar correctamente los paths de las imágenes o por cualquier otro motivo que se pueda necesitar.
Ejemplo:

def configure(self, d, n, dire):
    self.db = d
    self.notif = n
    self.dir = dire

show(self)
Si el Plugin que se esta creando utiliza alguna interfaz gráfica para interactuar con el usuario, este método retornara una instancia de la interfaz (objeto que debe extender de QWidget) para que PyTv lo cargue en una ventana y el usuario pueda tener acceso.

Ejemplo:

import pluginbase
import example
class Plugin(pluginbase.PluginBase):
    def show(self):
        widget = example.Main()
        return widget

threadMe(self)
Este método contiene el código que sera ejecutado cada vez que se corra el hilo principal de PyTv (si es que este Plugin fue configurado como _toThread=True).

Ejemplo:

def threadMe(self):
    #Do Staff
    image = self.dir + Plugin._icon
    self.notif.show_message('Welcome!', 'Example Plugin', image)

En este caso, el ejemplo toma la ruta de la carpeta Plugins recibida en "configure(...)" la concatena con la ruta del icono del plugin y emite un mensaje conteniendo ese icono mas el texto ingresado usando el sistema de notificaciones de PyTv.

Para instalar Nuevos Plugins, se puede ir al Menu "Complementos" (Plugins) de PyTv, elegir la opción "Gestor de Complementos" (Plugins Manager) y luego presionar el botón "Agregar Complemento" (Add Plugin), y PyTv nos mostrara una lista con los Plugins disponibles via web, los cuales al seleccionarlos y presionar "descargar" se descargaran e instalaran automáticamente en PyTv.


Si lo que queremos hacer es probar el Plugin que estamos desarrollando, solo es necesario copiarlo dentro de la carpeta "plugins" de PyTv que se encuentra en:


/home/[usuario]/.config/pytv/plugins

Uniendo Todo
Ahora vamos a unir todos estos conceptos para crear un Plugin de Ejemplo que va a tener la siguiente estructura:


__init__.py:
from main import Main



main.py:
from PyQt4 import QtGui, QtCore
class Main(QtGui.QWidget):
        def __init__(self):
        QtGui.QWidget.__init__(self)
        self.setWindowTitle('Plugin Example')
        self.setFixedWidth(500)
        v_box = QtGui.QVBoxLayout(self)
        v_box.addWidget(QtGui.QLabel('Example Plugin'))
        self.push = QtGui.QPushButton('Press to Close!')
        v_box.addWidget(self.push)
        self.connect(self.push, QtCore.SIGNAL("clicked()"), self.hide)


example_plugin.py:
import pluginbase
import example
class Plugin(pluginbase.PluginBase):
    _name = 'Example Plugin'
    _description = 'This plugin is just for example purpose'
    _icon = 'example/Goomba.png'
    _toThread = True
    _toShow = True

    _authors = ['Diego Sarmentero <diego.sarmentero@gmail.com>']

    def __init__(self):
        self.notif = None
        self.db = None
        self.dir = None

    def configure(self, d, n, dire):
        self.db = d
        self.notif = n
        self.dir = dire

    def show(self):
        ex = example.Main()
        return ex

    def threadMe(self):
        #Do Staff
        image = self.dir + Plugin._icon
        self.notif.show_message('Welcome!', 'Example Plugin', image)

Y Listo!!
Tenemos un Plugin que participa en el hilo principal de PyTv, emite notificaciones y tiene un interfaz gráfica para interactuar con el usuario.






Para ver un ejemplo donde se hace uso de la Base de Datos y las Notificaciones se puede consultar el código del Plugin: Recommender

El código completo del ejemplo puede descargarse desde el siguiente link: DESCARGAR