3ENGINE

Programación y otros cachivaches

Etiqueta: Python

Página 3/6

Tecnologia

pyqt: cómo hacer que QLabel sea clicable.


En Qt el widget QLabel no es clicable. Para emular el click del ratón basta añadir una propiedad que use la clase Signal y sobreescribir el evento mouseRelease de QLabel de modo que emita un señal clicked.

class QLabelClickable(QLabel):

    clicked = pyqtSignal()
    
    def __init__(self, *args):
        QLabel.__init__(self, *args)
   
    def mouseReleaseEvent(self, ev):
        self.clicked.emit()

Y este es el código para probarlo:

class MainWindow(QMainWindow):

    def label_clicked(self):
        print "click"
        
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)
        self.mylabel = QLabelClickable('click me!')
        self.mylabel.clicked.connect(self.label_clicked)
        self.setCentralWidget(self.mylabel)

app = QApplication(sys.argv)
main = MainWindow()
main.show()     
sys.exit(app.exec_())

En este otro ejemplo ahora QLabel incorpora una imagen y además hago uso del antiguo sistema para conectar eventos:

class MainWindow(QMainWindow):

    def label_clicked(self):
        print "click"
        
    def __init__(self, *args):
        QMainWindow.__init__(self, *args)
        self.mylabel = QLabelClickable('')
        self.mylabel.setPixmap(QPixmap('myimage.png').scaled(10, 10, Qt.KeepAspectRatio))
        self.connect(self.mylabel, SIGNAL('clicked()'), self.label_clicked)
        self.setCentralWidget(self.mylabel)

app = QApplication(sys.argv)
main = MainWindow()
main.show()     
sys.exit(app.exec_())




Tecnologia

Decoradores Python con parámetros


Ya expliqué en una entrada anterior que es eso de los decordadores Python. Si todavia no lo has hecho te recomiendo que leas el artículo antes de seguir.

Decoradores con parámetros

Hace unos días tuve la necesidad de pasar parámetros a un decorador. Queria crear un decorador que retornara un valor por defecto si la función decorada retorna valor nulo. Entonces me di cuenta que mi anterior artículo no cubria esta aspecto.

Este es mi decorador:

def default_value(value):
    def _default_value(func):
        def box(*args, **kwargs):
            result = func(*args, **kwargs)
            if not result:
                return value
            return result
        return box
    return _default_value

Tenemos una funcion un tanto maleducada 😉 que sólo saluda a personas que se llamen David. La vamos a decorar para que retorne un saludo por defecto en caso que retorne nulo:

@default_value('hola desconocido')
def saluda(nombre):
    if nombre == 'david':
        return 'hola david'

Un test:

print saluda('david')
print saluda('antonio')

Y esta es la salida:

hola david
hola desconocido

Otra posible aplicación de los decoradores con parámetros, es si por ejemplo queremos devolver un valor por defecto sólo si la llamada a un función genera una excepción.

El código completo:

def default_try_value(value):
    def _default_try_value(func):
        def box(*args, **kwargs):
            try:
                result = func(*args, **kwargs)
            except:
                return value
            return result
        return box
    return _default_try_value
  
@default_try_value('error')
def divide_entre_2(numero):
    return numero / 2
    
print divide_entre_2(16)
print divide_entre_2('david')

Y esta es la salida:

8
error



Tecnologia

Utilizar markdown en modulos Pypi


Es posible que una vez subido un módulo Python a Python Package Index notes que la descripción del módulo (README) que aparece en la página PyPi no esté bien formateada.

Si formateaste la descripción del módulo con Markdown, caso bastante probable si tienes las fuentes en Github, tienes que saber que PyPi únicamente acepta reStructuredTxt como formato de descripción.

Las solución drástica es cambiar la descripción al formato aceptado por PyPi. O bien puedes traducir «on fly» de un formato a otro en setup.py.

Para hacer esto, primero instala pandoc. Se trata de una utilidad que es capaz de convertir ficheros de un lenguaje de marcado a otro.

$ sudo apt-get install pandoc

Después instala el módulo Python pyandoc:

$ sudo pip install pyandoc

Por último modifica setup.py:

long_description = ''

try:
    import subprocess
    import pandoc

    process = subprocess.Popen(
        ['which pandoc'],
        shell=True,
        stdout=subprocess.PIPE,
        universal_newlines=True
    )

    pandoc_path = process.communicate()[0]
    pandoc_path = pandoc_path.strip('\n')

    pandoc.core.PANDOC_PATH = pandoc_path

    doc = pandoc.Document()
    doc.markdown = open('README.md').read()

    long_description = doc.rst

except:
    pass
   
setup(
    long_description=long_description  
    # y tus parámetros de configuración....
)

Las líneas de la 6 a la 16 averigua la ruta donde se encuentra pandoc instalado mediante una llamada al sistema del comando which. Un try/catch es necesario para garantizar la instalación del módulo mediante python setup.py install en un sistema sin pandoc instalado.




Tecnologia

Clases abstractas en Python


Python de forma nativa no soporta clases abstractas, pero eso no significa que no existan. A partir de la versión 2.6, Python trae el módulo abc (abstract base classes) de acuerdo a PEP 3119, que consigue emular la abstracción de clases mediante metaclases y decoradores Python.

python abstract class

Metaclases

Un buen artículo muy recomendable que consigue explicar el mundillo de las metaclases sin pegarse un tiro en la cabeza, es este de crysol Ahí va la virgen! Metaclases! (con Python). Pero para hacerse una idea, diremos que una metaclase es una clase cuyas instancias son clases en lugar de objetos.

Es decir, si para construir un objeto usas una clase, para construir una clase usas una metaclase. Es importante entender que en Python todo es un objeto (incluso las clases, los modulos, los tipos de datos, etc..) y por lo tanto las clases, al ser objetos, son instancias de una metaclase.

En el caso que nos ocupa, es interesante porque permite modificar el comportamiento o características de una clase en el momento de su creación.

Decoradores

Respecto a los decoradores pues prefiero recomendaros un artículo de la casa 😉 Decoradores Python. A modo de resumen diremos que un decorador Python permite añadir funcionalidad extra a una función mediante un wrapper (envoltorio).

Clases abstractas en Python

Para empezar. Definiremos un método abstracto mediante el uso del decorador @abstractmethod. Al instanciar la clase… ¡Sorpresa! ha funcionado. Python no se queja.

>>> import abc
... 
... class Vehicle(object):
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
... vehicle = Vehicle()
... vehicle.who_are_you()
I'm a vehicle and also an abstract method
>>>

Para que el invento funcione, la clase Vehicle tiene que heredar de la metaclase ACBMeta. Con un poco de ‘azucar sintáctico’ (¿what?) mediante __metaclass__ es fácil. Ahora Python hace lo esperado, se queja porque no es posible instanciar una clase con métodos abstractos.

>>> import abc
... from abc import ABCMeta
... 
... class Vehicle(object):
...     __metaclass__ = ABCMeta
...     
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
... vehicle = Vehicle()
... vehicle.who_are_you()
Traceback (most recent call last):
  File "<pyshell#6>", line 11, in <module>
    vehicle = Vehicle()
TypeError: Can't instantiate abstract class Vehicle with abstract methods who_are_you
>>>

Para curiosos. En el repositorio oficial está la implementación de la metaclase ABCMeta. Ahora vamos a crear una clase Car que herede de Vehicle y que sobreescriba el método abstracto.

>>> import abc
... from abc import ABCMeta
... 
... class Vehicle(object):
...     __metaclass__ = ABCMeta
...     
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
... class Car(Vehicle):
... 
...     def __init__(self):
...         Vehicle.__init__(self)
...         
...     def who_are_you(self):
...         print "I'm a car"
... 
... car = Car()
... car.who_are_you()
I'm a car
>>>

Como curiosidad, podemos hacer ‘trampas’ e invocar al método abstracto mediante el uso de super().

>>> import abc
... from abc import ABCMeta
... 
... class Vehicle(object):
...     __metaclass__ = ABCMeta
...     
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
... class Car(Vehicle):
... 
...     def __init__(self):
...         Vehicle.__init__(self)
...         
...     def who_are_you(self):
...         super(Car, self).who_are_you()
...         print "I'm a car"
... 
... car = Car()
... car.who_are_you()
I'm a vehicle and also an abstract method
I'm a car
>>>

Con el decorador @abstractproperty podemos crear propiedades abstractas. Aquí un ejemplo.

>>> import abc
... from abc import ABCMeta
... 
... class Vehicle(object):
...     __metaclass__ = ABCMeta
...     
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
...     @abc.abstractproperty
...     def color(self):
...         pass
...         
... class Car(Vehicle):
... 
...     def __init__(self):
...         Vehicle.__init__(self)
...         
...     def who_are_you(self):
...         print "I'm a car"
... 
...     @property
...     def color(self):
...         return "red"
... 
... car = Car()
... car.who_are_you()
... print "my color is", car.color
I'm a car
my color is red
>>> 

El anterior ejemplo definimos una propiedad de lectura. También es posible crear propiedades abstractas de lectura y escritura (getter & setter) pero esto solo es posible hacerlo sin ‘azúcar sintáctico’

>>> import abc
... from abc import ABCMeta
... 
... class Vehicle(object):
...     __metaclass__ = ABCMeta
...     
...     @abc.abstractmethod
...     def who_are_you(self):
...         print "I'm a vehicle and also an abstract method"
... 
...     def get_color(self):
...         pass
...     
...     def set_color(self, value):
...         pass
... 
...     color = abc.abstractproperty(get_color, set_color)
...         
... class Car(Vehicle):
... 
...     def __init__(self):
...         Vehicle.__init__(self)
...         self._color = "red"
...         
...     def who_are_you(self):
...         print "I'm a car"
... 
...     @property
...     def color(self):
...         return self._color
... 
...     @color.setter
...     def color(self, value):
...         self._color = value
... 
... car = Car()
... car.color = "blue"
... car.who_are_you()
... print "my color is", car.color
I'm a car
my color is blue
>>>