Gtkmm: Entry, Label, segnali, ustring

ho creato una semplice GUI con Glade.
ho creato due signal handler per i bottoni: uno esegue l’ inversione della ustring, l’ altro cancella i campi.

  • i parametri passati con il binding sono i puntatori ai widget.
    Notare anche l’ uso di ptr_fun per indicare il puntatore ad una funzione, a differenza di mem_fun che serve con i puntatori a metodi.
  • notare come viene invertita la ustring: non ho trovato di meglio che usare il metodo assign che supporta gli iteratori. Usando i reverse iterators si ottiene la stringa invertita.
  • il testo di una label di imposta/modifica con set_text e get_text.
  • per una entry il testo si trova in un buffer a cui si accede con get_buffer, i cui metodi get_text() e delete_text() consentono la manipolazione.

ecco il listato:

#include <gtkmm/application.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/entry.h>
#include <gtkmm/label.h>
#include <gtkmm/builder.h>
#include <iostream>


void onOK_clicked(Gtk::Entry *pE, Gtk::Label *pL)
{
	Glib::ustring rev = pE->get_buffer()->get_text();
	rev.assign(rev.rbegin(), rev.rend());
	pL->set_text(rev);
	std::cout << "OK !" << std::endl;
}

void onCancel_clicked(Gtk::Entry *pE, Gtk::Label *pL)
{
	pE->get_buffer()->delete_text(0, -1);
	pL->set_text("inserisci nuovo testo.");
	std::cout << "Cancel :-(" << std::endl;
}

int main(int argc, char** argv)
{
	Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

	Glib::RefPtr<Gtk::Builder> builder = Gtk::Builder::create_from_file("src/GUI.glade");

	Gtk::Button *pOk_button, *pCancel_button;
	builder->get_widget("button_OK",pOk_button);
	builder->get_widget("button_CANCEL", pCancel_button);

	Gtk::Entry *pEntry;
	Gtk::Label *pLabel;
	builder->get_widget("entry", pEntry);
	builder->get_widget("label", pLabel);

	pOk_button->signal_clicked().connect(sigc::bind<Gtk::Entry*, Gtk::Label*>( sigc::ptr_fun(&onOK_clicked), pEntry, pLabel) );
	pCancel_button->signal_clicked().connect(sigc::bind<Gtk::Entry*, Gtk::Label*>( sigc::ptr_fun(&onCancel_clicked), pEntry, pLabel) );

	Gtk::Window *window;
	builder->get_widget("window", window);
	return app->run(*window);
}

Signal handler con gtkmm

Dopo aver dichiarato un widget e una funzione void in una classe Gtk::Window

#include <gtkmm/button.h>
#include <gtkmm/window.h>

class HelloWorld : public Gtk::Window
{

public:
  HelloWorld();
  virtual ~HelloWorld();

protected:
  //Signal handlers:
  void on_button_clicked();

  //Member widgets:
  Gtk::Button m_button;
};

bisogna “collegare” la funzione al widget in modo che venga chiamata ogni volta che un segnale viene lanciato:

#include "helloworld.h"
#include <iostream>

HelloWorld::HelloWorld()
: m_button("Hello World")   // creates a new button with label "Hello World".
{
  ...
  m_button.signal_clicked().connect(sigc::mem_fun(*this,
              &HelloWorld::on_button_clicked));
  ...
}

void HelloWorld::on_button_clicked()
{
  std::cout << "Hello World" << std::endl;
}

Notare che l’ handler non ha parametri.

A volte si può usare uno stesso handler per più widget simili.
Allora si rende necessario il passaggio di parametri o argomenti.
Per passare argomenti, bisogna fare il binding:

...
void on_button_clicked(Glib::ustring, int val);
...
// sigc::bind<Tipo1, Tipo2, ...>(HANDLER, param1, param2, ...);
m_button1.signal_clicked().connect( sigc::bind<Glib::ustring, int>( sigc::mem_fun(*this, &HelloWorld::on_button_clicked), "xyz", 3) );

Un proxy basilare con Twisted Python

ho rielaborato questo esempio: http://stackoverflow.com/questions/6491932/need-help-writing-a-twisted-proxy

Nello script definisco alcune classi in un ordine ben preciso, per evitare errori di tipo
NameError: name ‘NomeDellaClasse’ is not defined.

Per capire come si imposta il proxy, iniziamo dalla classe proxy.Proxy:
questa eredita da twisted.web.http.HTTPChannel, e la documentazione spiega che per avviare il reactor bisogna chiamare la factory http.HTTPFactory(), dopodichè segnalare alla factory quale classe derivata da proxy.Proxy usare:

class MyProxy(proxy.Proxy):
    pass

f = http.HTTPFactory()
f.protocol = MyProxy
reactor.listenTCP(8888, f)
try:
	reactor.run()
except KeyboardInterrupt:
	reactor.stop()

Questo script definisce un proxy perfettamente funzionante.
Ma mancano varie funzionalità: queste sono definite nei metodi delle varie classi del modulo proxy.

Per usare le altre classi bisogna ridefinirle (override), a cominciare proprio da Proxy:

Proxy

nel suo codice sorgente si vede che c’è la variabile requestFactory, per specificare quale ProxyRequest usare.

ProxyRequest

Ridefinendo proxy.ProxyRequest abbiamo a disposizione i metodi per manipolare le richieste HTTP.
Dal suo codice sorgente si vedono 2 parametri da specificare, protocols e ports.
Il metodo ProxyRequest.process() crea la richiesta:
come si può vedere, le proprietà protocols e ports vengono usate per creare la clientFactory

class ProxyRequest(Request):
    protocols = {'http': ProxyClientFactory}
    ports = {'http': 80}
    ...
    def process(self):
        parsed = urlparse.urlparse(self.uri)
        protocol = parsed[0]
        host = parsed[1]
        port = self.ports[protocol]
        ...
        class_ = self.protocols[protocol]
        clientFactory = class_(self.method, rest, self.clientproto, headers,
                               s, self)
        self.reactor.connectTCP(host, port, clientFactory)

ProxyClientFactory

Con il protocollo MyProxyClientFactory registrato in MyProxyRequest, si gestisce la parte client del proxy, cioè le risposte http.
Ridefinendo ProxyClientFactory si può specificare una classe figlia di ProxyClient.

ProxyClient

Ridefinendo ProxyClient si ha accesso ai metodi per manipolare le risposte.
Vedere il codice sorgente o la documentazione.

Notare che è necessario richiamare gli handler della classe genitrice !

Esempio

Tutto quanto detto finora si traduce nel seguente script:

""" file myproxy.py """
from twisted.web import http, proxy
from twisted.internet import reactor
from twisted.python import log
import sys

""" Salvo lo stdout prima di avviare il logging di twisted """
schermo = sys.stdout

log.startLogging(open('myproxy.log', 'w'))

class MyProxyClient(proxy.ProxyClient):
	def handleHeader(self, key, value):
		s = "Ricevuto header: " + key
		s+= ", " + value
		schermo.write(s + "\n")
		# richiamare il metodo della classe genitrice
		proxy.ProxyClient.handleHeader(self, key, value)

class MyProxyClientFactory(proxy.ProxyClientFactory):
	protocol = MyProxyClient

class MyProxyRequest(proxy.ProxyRequest):
	protocols = {'http': MyProxyClientFactory}
	ports = {'http': 80}

# non è necessario:
#	def process(self):
#		proxy.ProxyRequest.process(self)

class MyProxy(proxy.Proxy):
	requestFactory = MyProxyRequest


f = http.HTTPFactory()
f.protocol = MyProxy
reactor.listenTCP(8888, f)

try:
	reactor.run()
except KeyboardInterrupt:
	reactor.stop()

Appunti AJAX

Gestire eventi su HTML dinamico

Non so quale possa essere il modo migliore per gestire gli eventi sull’ HTML ricevuto tramite XMLHttpRequest, nel senso che ci sono tanti modi diversi per farlo, ma non trovo la soluzione migliore.
Forse jQuery permette una soluzione “semplice”, ma io vorrei usare solo JavaScript con PrototypeJS.

Primo caso

Ricevo HTML con una richiesta gestita da Ajax.Updater, e la risposta contiene javascript inline:

new Ajax.Updater('div_id', '/path/to/get_smtg.php', {
// notare evalScripts
  parameters: { param1: 'value1', evalScripts: true) }
});

ogni blocco <script> viene “valutato” da javascript.
Questo può essere un buon metodo, ma ha 2 difetti:

  • non c’è separazione del codice, dato che javascript e PHP risultano mescolati
  • ogni volta che viene chiamato Ajax.Updater deve essere valutato ogni script ricevuto (ad esempio gli event listeners vengono rimossi e ricollegati)

Un pregio è quello di centralizzare la logica che gestisce l’ HTML ricevuto dinamicamente.

Secondo caso

Per ovviare ai precedenti difetti si può pensare alla delegazione degli eventi:
invece di aggiungere i listener all’ HTML ricevuto, si pone un listener sul suo contenitore.
Non sono pienamente convinto di queso metodo: non mi piace che ci siano degli script attivi su un contenuto che non esiste e che potrebbe non essere raggiunto con AJAX.
Si può mitigare questo comportamento registrando i listener con la classe Event.Handler di prototypeJS:
il costruttore istanzia un oggetto “handler” che però non osserva gli eventi finchè non viene chiamato il metodo start

var handler = new Event.Handler(element, eventName[, selector], callback);
handler.start();
...
handler.stop();

Terzo caso

Dovrei vedere come realizzare un widget in cui fondere HTML e JavaScript.
http://www.amazon.com/Developing-Widget-HTML-JSON-AJAX/dp/1450502288
http://developingwebwidgets.blogspot.it

Restituire HTML e JavaScript da PHP

Per restituire JS inline al codice HTML bisogna costruire la stringa della risposta.

heredoc e nowdoc

PHP permette di assegnare testo multilinea ad una variabile stringa tramite gli heredoc e, dalla versione 5.3.0, anche tramite i nowdoc:
la differenza fra i due è che heredoc subisce il parsing di PHP, mentre nowdoc no.

// heredoc:
$name = 'MyName';
$variable = "text";
echo <<<EOT
My name is "$name". I am printing some $variable.
This should print a capital 'A': \x41
EOT; // notare il punto-e-virgola

Output:

My name is “MyName”. I am printing some text.
Now, I am printing some Bar2.
This should print a capital ‘A’: A

Invece con nowdoc:

// nowdoc:
$name = 'MyName';
$variable = "text";
// notare il single-quote
echo <<<'EOT'
My name is "$name". I am printing some $variable.
This should not print a capital 'A': \x41
EOT;

Output:

My name is “$name”. I am printing some $variable.
This should not print a capital ‘A’: \x41

Includere un file esterno

Il comando include di PHP legge un file e lo riporta nel file attuale.
Ci sono però varie problematiche da tenere presente:

  • una è include_path
  • poi c’è il comportamento del return per assegnare l’ output ad una variabile
  • infine si può usare l’ output buffering per ricevere l’ output di include

Vedere il manuale di include.

Oppure bisogna leggere un file con fopen(), e restituire il suo contenuto.