Xmlexe
Da GLComo.
xmlexe è uno script python che carica un file xml il quale definisce un'interfaccia Qt4, scriptabile via python
Indice |
Ultima versione:
Cambiamenti:
v.0.2.2
- il parametro "_parent" è obbligatorio se l'attributo "init" e' specificato, e la classe si aspetta un oggetto parent
- sistemato un bug che dava un contesto errato ai tag <script>
- leggermente migliorati i messaggi di errore
- supporto agli slot eventi con paramentri
- ogni QWidget e' imparentato al QWidget più vicino nell'albero, invece che alla radice
v.0.2.1
- E' possibile creare oggetti oltre che da QtGui, anche da QtCore (come QTimer)
- l'attributo "onevent" collega al segnale "event()" oltre che al segnale "evented()", così è possibile collegarsi a eventi come timeout() e clicked()
- Usa box di messaggi grafici per segnalare messaggi ed errori, invece di scrivere a console
- Supporto iniziale ai namespace nell'xml, per caricare custom widget da un file py separato
v.0.2
- attributo 'init' per il costruttore delle classi
- aggiunti metodi per usare xmlexe come modulo
v.0.1.1
- prima versione
Documentazione
Requisiti
Per funzionare xmlexe necessita di
- python (testato su python2.5 e python2.6)
- PyQt4
L'XML
L'xml letto da xmlexe ricalca molto la struttura delle Qt4.
E' stato creato avendo usato sia XUL di mozilla che flex di adobe, ma con l'intenzione di utilizzare Python per lo scripting.
Ogni tag, fondamentalmente, richiama il nome della classe Qt4.QtGui che si vuole creare:
<QDialog />
o
<QPushButton />
Vedi la documentazione delle Qt per informazioni sulle classi
Metodi e proprietà
I metodi e le proprietà di una classe possono essere utilizzati in xml come attributi o come tag figli:
<QPushButton text="Exit" />
o
<QPushButton>
<text><![CDATA[Exit]]></text>
</QPushButton>
Questo codice XML farà si che xmlexe cerchi una proprietà o un metodo di QPushButton chiamati 'text' o 'setText' o 'addText'
La seconda forma di chiamare un metodo puo' essere utilizzata per passare un widget come argomento a un metodo che se ne aspetta uno:
QBoxLayout::addWidget(QWidget *widget)
in XML puo' essere usato così:
<QBoxLayout init="TopToBottom">
<widget>
<QPushButton text="Exit" />
</widget>
</QBoxLayout>
Questo passera' il bottone QPushButton al metodo addWidget di QBoxLayout.
(init è un attributo speciale, usato per il costruttore della classe, vedi sotto)
Tipi di dato
Di default tutti i valori degli attributi sono passati come stringa ai metodi corrispondenti.
Se un metodo si aspetta un booleano o un numero come arogomento, si puo' definire il tipo di dato nel valore dell'attributo, anteponendo il tipo al valore:
- attrib="str:testo" ritorna la stringa "testo", equivale ad attrib="testo"
- attrib="int:numero" ritorna il valore di "numero", es attrib="int:2"
- attrib="bol:booleano" ritorna un valore booleano. Puo' essere "True" o "False", es attrib="bol:True"
Espressioni Python come valore
Un'attributo puo' contenere codice Python, racchiuso tra { e }.
Il codice verrà eseguito una volta completato il parsing del file xml, prima di lanciare l'evento "onLoad".
es.:
<QLabel text="{str(5+3)}" />
Il costruttore
La maggior parte delle classi QtGui usate hanno come unico parametro del costruttore il widget genitore, per cui xmlexe quando crea un'istanza passa il l'oggetto più vicino dell'albero xml che derivi da QWidget come parent. Nel caso sia necessario passare altri parametri al costruttore, si usa l'attributo init, nella forma
init="<valore>,<valore>,<valore>"
<valore> ha la stessa forma degli attributi, per cui puo' essere un testo, o un numero se preceduto da int: o un booleano se preceduto da bol: Si puo' racchiudere la stringa tra apici, se si vuole inserire una virgola nel testo da passare come parametro:
init = " 'testo, virgola' "
Se <valore> è una stringa, viene controllato se il nome rappresenta un enumeratore della classe che si sta costruendo. In caso positivo, verra' passato l'enumeratore invece del valore stringa es, il costruttore di QBoxLayout è:
QBoxLayout ( Direction dir, QWidget * parent = 0 )
con
enum Direction { LeftToRight, RightToLeft, TopToBottom, BottomToTop }
inPyQt si crea con
bl = QBoxLayout(QBoxLayout.TopToBottom)
in XML
<QBoxLayout init="TopToBottom" />
"TopToBottom" verrà automaticamente tradotto in QBoxLayout.TopToBottom
E' possibile anche passare un'espressione Python, racchiudendola tra { e }.
Tale espressione verrà valutata nel momento del parsing del tag, ovvero quando viene creata l'istanza, per cui saranno validi solo gli script e gli oggetti già creati fino a quel momento.
Se il costruttore richiede l'oggetto genitore e non si vuole specificarne uno in particolare, si usa in init il parametro _parent, che verrà sostituito dal valore di default
<QPushButton init="{form.icona},'Premimi, ti piacerà',{form.id_parent}" />
o
<QPushButton init="{form.icona},'Premimi, ti piacerà',_parent" />
Eventi
Gli eventi possono essere definiti in attributi, come "onclick". Xmlexe creerà lo slot "onclick" per l'evento "clicked".
Il valore dell'attributo puo' essere del codice Python eseguito dallo slot:
Es:
<QPushButton onclick="{ self.setText('ouch!') }" />
creerà nell'istanza di QPushButton il metodo onclick(self) e lo collegherà con l'evento clicked()
oppure il nome di un metodo da collegare:
Es:
<script> def onButtonClicked(self): print "ouch!" </script> <QPushButton onclick="form.onButtonClicked" />
collegherà l'evento clicked() con la funzione onButtonClicked
Quest'ultima forma di collegare gli eventi permette utilizzare gli eventi con parametri, semplicemente inserendo nella docstring della funzione
"@signal (<type>,<type>...)"
es:
per ricevere il segnale "triggered(bool)" (documentazione Qt)
<script> def trig(self, param): """@signal (bool)""" print param </script> <QAction ontrigger="form.trig"/>
Vedi la documentazione delle Qt per informazioni sui segnali
Script
E' possibile inserire del codice Python con il tag <script>. Opzionalmente si puo' specificare type="text/python" Le funzioni dichiarate all'interno del tag <script> dovranno riceve il parametro self, dato che diventeranno metodi dell'istanza della classe creata dal tag genitore.
es:
<QButton id="btn1">
<script>
def calculateSum(self):
print 2+3
</script>
</QButton>
Si potra' quindi fare riferimento a calculateSum come metodo di btn1
Ogni oggetto creato ha una proprietà form che si riferisce al widget radice della finestra.
L'oggetto radice a sua volta ha un'attributo per ogni figlio creato, chiamato con il nome dell'attributo 'id' del figlio.
Per cui, per chiamare calculateSum da un'altro widget, si dovrà scrivere
self.form.btn1.calculateSum()
Namespace
E' possibile usare i namespace xml per importare moduli python che contengono classi che definiscono widget custom. Se ho un file mycustom.py che contiene la classe CustomWidget, posso usarla nell'xml così:
<QDialog xmlns:mc="mycustom"> <mc:CustomWidget /> </QDialog>
Esempi
Il più classico
<?xml version="1.0" encoding="UTF-8"?> <QLabel text="Ciao, mondo!" />
Un bottone e un'evento
<?xml version="1.0" encoding="UTF-8"?>
<QPushButton text="Cliccami per uscire" onclick="{self.close()}"/>
Una finestra. Impostiamo dimensioni e titolo della finestra
<?xml version="1.0" encoding="UTF-8"?> <QDialog x='0' y='0' width='350' height='200' windowTitle='Finestra di esempio'> </QDialog>
La stessa finestra. Il gestore "onload", un'etichetta e un po' di script python
<?xml version="1.0" encoding="UTF-8"?>
<QDialog x='0' y='0' width='350' height='200' windowTitle='Finestra di esempio' onload="self.onload">
<script>
<![CDATA[
def onload(self):
self.lbl1.setText("Ciao, mondo!")
]]>
</script>
<QLabel id="lbl1" />
</QDialog>
Un'etichetta e un timer
<?xml version="1.0" encoding="UTF-8"?>
<QLabel x='0' y='0' width='350' height='100' onload="self.onload" >
<script>
<![CDATA[
from datetime import datetime as dt
def onload(self):
self.update()
self.t1.start()
def update(self):
self.setText("Ciao, mondo! %s" % self.dt.now())
]]>
</script>
<QTimer id="t1" interval="int:1000" singleShot="bol:False" ontimeout="form.update" />
</QLabel>
Cose da fare
- Trovare una maniera per definire eventi che supportano parametri
- Usare i namespace per importare moduli nel globals(), senza che siano per forza widget
- Se scrivessi nell'xml
xmlns:dt="datetime.datetime"
- potrei eseguire
from datetime import datetime as dt
- e inserire "dt" nei globals(). A questo punto in qualsiasi script dell'xml potrei avere
dt.now()
- Al momento è possibile importare moduli nei tag <script>. Questi moduli saranno pero' locali all'oggetto creato dal tag che contiene il tag <script>. Con questo sistema dei namespace i moduli importati sarebbero disponibili globalmente.
- Pare che non riesca a tirar fuori via parser sax i namespace che non siano mai utilizzati veramente da un tag. regexp?

