Archive for the ‘Python’ Category

Buona nerdlettura.

Il linguaggio Python offre, fra le sue librerie standard, almeno due opzioni per realizzare la temporizzazione delle funzioni o detto altrimenti, per eseguire una funzione ad un certo orario o dopo un tot di secondi.

La libreria “sched” (http://www.python.org/doc/2.5.2/lib/module-sched.html) è la prima opzione ma non ci piace perché non consente di restituire il controllo al main durante l’attesa.

La seconda, focus di questo articolo, è la funzione Timer della libreria “threading” (http://docs.python.org/library/threading.html).

Osservando il codice di threading.py (che trovate ad esempio in /usr/lib/python2.6/):

[...]
def Timer(*args, **kwargs):
    return _Timer(*args, **kwargs)

class _Timer(Thread):
    """Call a function after a specified number of seconds:

    t = Timer(30.0, f, args=[], kwargs={})
    t.start()
    t.cancel() # stop the timer's action if it's still waiting
    """

    def __init__(self, interval, function, args=[], kwargs={}):
        Thread.__init__(self)
        self.interval = interval
        self.function = function
        self.args = args
        self.kwargs = kwargs
        self.finished = Event()

    def cancel(self):
        """Stop the timer if it hasn't finished yet"""
        self.finished.set()

    def run(self):
        self.finished.wait(self.interval)
        if not self.finished.isSet():
            self.function(*self.args, **self.kwargs)
        self.finished.set()
[...]

si nota che il costruttore prende in ingresso:
– interval: il numero di secondi dopo i quali eseguire la funzione
- function: il nome della funzione
- args: lista opzionale di argomenti della funzione
- kwargs: dizionario opzionale di argomenti con nome

Nel metodo run, si può infatti notare che la chiamata: self.function(*self.args, **self.kwargs) accetta una lista e/o un dizionario.

Per capire come utilizzarli si può far riferimento a questo articolo:
the mysterious miss args and mister kwargs

Vediamo ora alcuni esempi di utilizzo di un Timer.

import time
from threading import Timer

def print_time():
    print "From print_time", time.time()

def print_some_times():
    print "From print_some_times", time.time()
    Timer(2, print_time).start()

print_some_times()

Dentro print_some_times() invochiamo print_time() che verrà eseguita tra 2 secondi.

import time
from threading import Timer

def print_time(arg):
    print arg
    print "From print_time", time.time()

def print_some_times():
    a = ["my_arg"]
    print "From print_some_times", time.time()
    Timer(2, print_time, a).start()

print_some_times()

Stesso di sopra ma con un parametro passato alla funzione print_time().
Nota bene: il parametro – anche se singolo – deve essere passato come lista di elementi.

Il passaggio di più parametri può avvenire utilizzando:
def print_time(arg1, arg2, ..., argN)
con relativo:
a = ["my_arg1", "my_arg2", ..., "my_argN"]
oppure con la sintassi:
def print_time(*args)
e relativo:
a = ["my_arg1", "my_arg2", ..., "my_argN"]

Lo script diventa quindi:

import time
from threading import Timer

def print_time(*args):
    print args
    print "From print_time", time.time()

def print_some_times():
    a = ["my_arg1", "my_arg2", "my_arg3", 1, 5, "ancora_stringa"]

    print "From print_some_times", time.time()
    Timer(2, print_time, a).start()

print_some_times()

e così via.
Lo stesso discorso vale per kwargs usando la sintassi:
b = {"name1":"value1", "name2":2, ...}

import time
from threading import Timer

def print_time(*args, **kwargs):
    print args
    print kwargs
    print "From print_time", time.time()

def print_some_times():
    a = ["quattro", "otto", "quindici", 16, 23]
    b = {"name1":"jack", "name2":"kate", "name3":"sawyer", "number": 42}

    print "From print_some_times", time.time()
    Timer(2, print_time, a, b).start()

print_some_times()

Buona nerdpratica.

And here goes another UI article from Code Zero: CPS, continuation passing style, which is powerful Pattern Design, that I would like to integrate in WPF while designing with M-V-VM pattern… let’s see something…in italian ;)

Parlando di UI Responsiveness, cioè la capacità dell’interfaccia utente di rimanere sempre attiva, ci inoltriamo ora in un campo ancora in piena esplorazione: i Design Patterns. I design patterns sono delle tecniche che ci consentono di modellare un software in maniera organica. Tra i più famosi nel .NET framework è il M-V-VM (Model-View – ViewModel), ma sta prendendo piede anche il CPS (continuation passing style), che interpreta una azione (ad esempio un Click) come continuazione di un altra azione.
E’ un concetto abbastanza complesso da capire, ma vedremo ora un esempio un po’ più chiarificatore, dal quale si potrà facilmente dedurre il perchè venga usato spesso per la UI responsiveness.
Supponiamo di avere un form vuoto sul quale attacchiamo un pannello che chiameremo ButtonPanel.

Ora se volessimo stampare sulla Console il testo della TextBox al click del bottone avremo un codice del genere:

private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine(textBox1.Text);
}

Ma significa che tutto è demandato al ButtonPanel, e se volessimo avere qualcosa di più riusabile sarebbe difficile. Quello che si vuole fare invece è demandare a Form1 il controllo della stampa a console e lasciare a a Button Panel solo la gestione del click. Cioè intendiamo la stampa a Console come la continuazione di un evento scatenato dal click, gestita però in maniera completamente autonoma da Form1, quindi questa azione potrà esistere solo se Form1 lo desidera, non legata indissolubilmente a ButtonPanel.
Quindi avremo che ButtonPanel eseguirà solo l’azione e diventerà così:

public partial class ButtonPanel : UserControl
{
Action<object> _onTextAction;

public ButtonPanel(Action<object> textAction)
{
InitializeComponent();

_onTextAction = textAction;
}

private void button1_Click(object sender, EventArgs e)
{
_onTextAction(textBox1.Text);
}
}

Mentre Form1, cioè il chiamante sarà così:

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

ButtonPanel panel = new ButtonPanel(s => OnButtonClick(s));

this.Controls.Add(panel);
}

private void OnButtonClick(object text)
{
Console.WriteLine(text);
}
}

Si noti anche l’uso delle Lambda Expression nel parametro di ButtonPanel, che, in questo caso, ci consentono una flessibilità ancora maggiore sulla chiamata.
Ecco che in pochissime righe di codice otteniamo un esempio di classe altamente riusabile, fatta in CPS, che destina il compito di gestire la stampa a Form1, e il compito di chiamarla a ButtonPanel. In questo caso ButtonPanel è perfetta, sarà difficile che si rompa in sè e per sè, piuttosto sarà sbagliato il passaggio di parametri, ma ciò non compete ButtonPanel.

Il mio primo articolo si concentrerà sulla creazione di un semplice script in Python, avviabile solo con la diretta esecuzione (doppio click sul file .py), utile per cliccare ripetutamente su uno o più determinati punti dello schermo, sulle finestre, bottoni, banner, etc. Una particolarità di questo script è quella di “sentire” gli input provenienti dalla tastiera anche se la finestra non è “a fuoco” cioè anche se non è l’attuale finestra in primo piano e attiva.
In breve, vengono creati due thread che comunicano mediante un buffer condiviso (come nel classico problema produttore/consumatore):
- t_keyboard: si occupa di ascoltare gli eventi della tastiera (i tasti premuti). È il produttore e inserisce i caratteri letti nel buffer
- t_mouse: gestisce il mouse, catturandone la posizione, spostandolo e facendolo cliccare. Si tratta del consumatore.

Le librerie/moduli accessorie sono:
- PyWin32
- PyHook
- SendKeys


"""
Project Name:....Aut0 P0int & Click v1.0
Author:..........Mauro "Baba" Mascia
Date:............2009/02
Thanks to:
  - Peter Parente
  - "BlueKitties" for the article "Mouse/Keyboard control scripting module."
  - prof. Matteo Pradella

"""
from ctypes import*
from SendKeys import*
from pyHook import *
from time import strftime
from time import sleep
from threading import Thread
from threading import Lock
import pythoncom
import sys

"""
    Create a thread that handle the keyboard
    All key pressed are stored in a shared buffer
"""
class t_keyboard(Thread):
    def __init__(self, buff):
        Thread.__init__(self)
        self.buff = buff

    def run(self):
        def OnKeyboardEvent(event):
            char = chr(event.Ascii)

            if char in ['r', 'd', 'q']:
                if not self.buff.put(char):
                    print "full buffer"

            if event.Key == 'Escape':
                sys.exit()

            return True

        hm = HookManager()
        hm.KeyDown = OnKeyboardEvent
        hm.HookKeyboard()   # set the hook

        # Wait forever and print keys
        pythoncom.PumpMessages()

"""
    Create a thread that handle the mouse
"""
class t_mouse(Thread):
    def __init__(self, buff):
        Thread.__init__(self)
        self.buff = buff

    def run(self):
        class POINT(Structure):
            _fields_ = [("x", c_ulong),("y", c_ulong)]

        #Gets the current mouse position
        def getpos():
            global pt
            pt = POINT()
            windll.user32.GetCursorPos(byref(pt))
            return pt.x, pt.y

        #Calculate how many minutes are now
        def minNow():
            h_now = int(strftime("%H")) #hours
            m_now = int(strftime("%M")) #mins
            t_now = h_now * 60 + m_now  #time in mins
            return t_now

        #Moves the cursor to x,y cordinate
        def move(x,y):
            windll.user32.SetCursorPos(x,y)

        #Clicks in the actual mouse position
        def click():
            LEFTDOWN   = 0x00000002
            LEFTUP     = 0x00000004
            windll.user32.mouse_event(LEFTDOWN,0,0,0,0)
            windll.user32.mouse_event(LEFTUP,0,0,0,0)

        print "Punta e clicca fino alle:"
        while True:
            h_end = int(raw_input("ora:"))
            if(h_end <= 24 and h_end > 0):
                break
        while True:
            m_end = int(raw_input("min:"))
            if(m_end <= 60 and m_end >= 0):
                break

        t_end = h_end*60+m_end

        print "\n------\nPremi:\n"
        print "- \'r\' per impostare le coordinare del mouse"
        print "- \'d\' per cominciare la riproduzione"
        print "- \'q\' per arrestare la riproduzione"
        print "- Esc per uscire"
        print "\n------\n"

        coords = []
        while True:
            char = self.buff.get() #reads from buffer

            if char == 'r':
                x, y = getpos()  #gets current position
                print "Catturata coordinata X:%d,Y:%d" %(x,y)
                coords.append((x, y))
            elif char == 'd' and len(coords)>0:
                print "Riproduzione..."
                while minNow() < t_end:
                    for x,y in coords:
                        move(x,y)
                        click()
                        sleep(0.08)
                    if self.buff.get() == 'q':
                        print "stopped"
                        break
                break
            else:
                """
                    sleep for a while if char isnt "r" or "d"
                    this is foundamental to release CPU's resources
                """
                sleep(0.001)

class Buffer(object):
    def __init__(self, size) :
        self.data = [None for x in xrange(size)]
        self.size = size
        self.head = self.tail = 0
        self.the_lock = Lock()

    def put(self, x):
        self.the_lock.acquire()
        if self.data[self.tail] != None :
            self.the_lock.release()
            return False # full
        self.data[self.tail] = x
        self.tail = (self.tail+1) % self.size
        self.the_lock.release()
        return True

    def get(self) :
        self.the_lock.acquire()
        if self.data[self.head] == None :
            self.the_lock.release()
            return None # empty
        v = self.data[self.head]
        self.data[self.head] = None
        self.head = (self.head+1) % self.size
        self.the_lock.release()
        return v

print "Aut0 P0int & Click v1.0\n"
buf = Buffer(100)
keyboard = t_keyboard(buf)
mouse = t_mouse(buf)
keyboard.start()
mouse.start()
Stats
Categories