Ho messo sul sito, una prima versione di Chiamami, una semplice agenda telefonica. Quanto prima farò una pagina di documentazione. Al momento è possibile scaricare i sorgenti.
-
0 comments -
Durante lo sviluppo di QtNotes, mi sono imbattutto nella necessitàdi utilizzare la try icon. Questa icona rimane nel vassoio di sistema e consente di tenere l’applicazione minimizzata funzionante pur senza lasciarla visibile.
Usare le try icon in PyQt però è leggermente diverso da come si farebbe in C++ (linguaggio nativo delle Qt). Si può dividere il lavoro in due parti:
- praparare la finistra principale a gestire la try-icon
- creare l’icona e connettere i relativi signal alle funzioni
Il primo punto serve in quanto bisogna implementare il metodo virtuale closeEvent, che viene generato quando viene chiusa la finestra. Questo è necessario in quanto in PyQt4 non è possibile l’ereditarietà multipla. Usando i file .ui per l’interfaccia non è possibile derivare la finestra principale da QMainWindow. Questo si risolve derivando una classe da QtGui.QMainWindow.
class MainWindow(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
def closeEvent(self, ev):
self.hide()
systray.showMessage('QtNotes', 'Application minimized')ev.ignore()
def ShowWindow(self):
self.show()
che chiameremo MainWindow. In questa classe ridefiniamo l’evento closeEvent per nascondere la finestra, mostrare un messaggio (facoltativo ma utile) ed in seguito ignorare l’evento. Questo serve per fare in modo che una volta chiusa la finestra premento il pulsante con la X, la finestra non venga chiusa ma <br />solo nascosta. La funzione ShowWindow serve unicamente per rendere la finestra nuovamente visibile.
In C++ si sarebbe usato un altro metodo. Potendo sfruttare l’ereditarietà multipla, si sarebbe fatta una cosa tipo:
class QtNotes(QtGui.QMainWindow, UI_MainWindow)
Una volta preparata questa classe, si procede come al solito quando si usano i file .ui, tenendo presente che la finestra va creata istanziando la classe MainWindow e non la QtGui.QMainWindow. A questo punto siamo pronti a creare ed utilizzare la try icon. Il codice minimo è questo:
icon=QtGui.QIcon('pixmaps/todo_list.png') systray=QtGui.QSystemTrayIcon(icon) menu = QtGui.QMenu() showAction = menu.addAction('Show') sep1 = menu.addSeparator() quitAction = menu.addAction('Quit') QtCore.QObject.connect(quitAction, QtCore.SIGNAL("triggered()"), ui.QuitApp) QtCore.QObject.connect(showAction, QtCore.SIGNAL("triggered()"), window.ShowWindow) systray.setContextMenu(menu) systray.show()che crea una try-icon, gli associa un context menù (che in questo caso ha solo due voci) e connette i relativi slot alle funzioni che ci interessano. Con questo la creazione di una try-icon funzionante è completa. L’unica cosa che rimane fuor è la possibilità di associare la try icon agli eventi tipo click e double-click del pulsante sinistro del mouse per fargli eseguire delle azioni. Il context menù infatti risponde al tasto destro del mouse.
-
Chi lavora con i toolkit grafici sa cosa sono dialog modali e non modali. In breve:
- Un dialogo modale non consente di interagire con l’applicazione che l’ha aperto
- Un dialog non modale lo consente
Per poter avere più dialoghi non modali, sembra che con le Qt (e PyQt4) si debba usare il seguente codice:
dlg = QtGui.QDialog()
dlggui = Ui_dialog()
dlggui.setupUi(dlg)
dlg.show()
dlg.exec_() -
Con la versione 4 delle Qt, non è più possibile avere le toolbar flottanti. Per ovviare al problema si può usare un altro widget, il QDockWidget, che fa proprio questo. Inoltre consente di inserire anche dei widget custom direttamente da Qt-Designer, cosa che non è possibile con le Toolbar. C’è però un problema: le toolbar, quando venivano spostate (es, dall’alto a sinistra) si ridimensionavano da sole e rimettevano a posto il layout. Il QDockWidget no. Però a tutto si può ovviare, quindi con un po’ di codice si supera anche questo problema. Prima di tutto bisogna creare nel QDockWidget un layout di tipo grid in cui vengono posizionati i controlli.Per un baco nel Pyuic 4, si deve aggiungere il QDockWidget alla mainwindow dell’applicazione manualmente, con questa riga di codice
window.addDockWidget(QtCore.Qt.RightDockWidgetArea, ui.dockWidget)
Una volta che tutto è pronto si può cominciare a risolvere il problema del layout.Per prima cosa bisogna connette il QDockWidget al segnale TopLevelChanged, in questo modo:
QtCore.QObject.connect(ui.dockWidget,QtCore.SIGNAL(“topLevelChanged(bool)”),ui.select)
Questo fa in modo che quando cambiate posizione al QDockWidget, venga chiamata il metodo select (o come decidete di chiamarla, magari qualcosa del tipo relayout). Il metodo in questione semplicemente ridispone i controlli a seconda del fatto che il QDockWidget sia da impaginare in verticale (quindi è “dockato” sul lato destro o sinistro) o in orizzontale (quindi sia “dockato” in alto o in basso).Il codice è banale:
def select(self):
ui.gridlayout.removeWidget(ui.comboBox)
ui.gridlayout.removeWidget(ui.toolButton)
ui.gridlayout.removeWidget(ui.toolButton_2)
ui.gridlayout.removeWidget(ui.toolButton_3)area = window.dockWidgetArea(ui.dockWidget)
if area == QtCore.Qt.BottomDockWidgetArea or area == QtCore.Qt.TopDockWidgetArea:
ui.gridlayout.addWidget(ui.comboBox, 0, 0)
ui.gridlayout.addWidget(ui.toolButton, 0, 1)
ui.gridlayout.addWidget(ui.toolButton_2, 0, 2)
ui.gridlayout.addWidget(ui.toolButton_3, 0, 3)
if area == QtCore.Qt.LeftDockWidgetArea or area == QtCore.Qt.RightDockWidgetArea:
ui.gridlayout.addWidget(ui.comboBox, 0, 0)
ui.gridlayout.addWidget(ui.toolButton, 1, 0)
ui.gridlayout.addWidget(ui.toolButton_2, 2, 0)
ui.gridlayout.addWidget(ui.toolButton_3, 3, 0)Semplicemente in caso di cambio tra verticale/orizzontare, tutti i controlli vengono riarrangiati (in questo caso) su una riga o su una colonna. In caso di più colonne e/o righe il codice non diventa più complicato, a meno che non si decida di fare qualcosa di dinamico.Rimangono fuori alcuni aspetti tipo ridimensionare correttamente il QDockWidget una volta ridisposti i controlli o eliminare le righe/colonne non utilizzate, ma la logica è questa.Un possibile miglioramento sarebbe quello di usare un layout verticale o orizzontale al posto della griglia
-
Se si vuole fare in modo che una grid (QTableWidget o simili) sia attiva ma non editabile o editabile solo in seguito a certi eventi, basta impostare la proprietà editTriggers al valore appropriato.I valori elencati sono i valori che possono essere selezionati, anche in combinazione tra di loro:
QAbstractItemView::NoEditTriggers Nessun edit QAbstractItemView::CurrentChanged L’editing inizia quando l’item corrente è¨ cambiato QAbstractItemView::DoubleClicked L’editing inizia quando si esegue un doppio click sull’item selezionato QAbstractItemView::SelectedClicked L’editing inizia quando si esegue un singolo click sull’item selezionato QAbstractItemView::EditKeyPressed L’editing inizia quando il tasto di editing predefinito èpremuto sull’item corrente QAbstractItemView::AnyKeyPressed L’editing inizia quando viene premuto un tasto sull’item corrente QAbstractItemView::AllEditTriggers L’editing inizia per tutti i valori sopra riportati -
Come ogni programma che si rispetti, anche quelli scritti in PyQt4 hanno la possibilità di essere localizzati.Per poter fare la traduzione delle varie stringhe bisogna seguire vari passi:
1 – Preparare il file delle traduzioni
Il file delle traduzioni è un file che contiene le stringhe in una lingua base (esempio inglese) e le corrispondenti traduzioni nella lingua in cui si vuole localizzare il programma.La struttura del file è la seguente:<!DOCTYPE TS><TS><context> <name>D_MainWin</name> <message> <source>Skipper</source> <translation>Skipper</translation> </message> </context></TS>dove name indica la sezione di cui si parla (ne parliamo dopo), source indica la stringa di default e translation indica la stringa tradotta. Il resto è del formato del file.Una precisazione name indica il nome dell’oggetto che vogliamo localizzare, semplicemente il valore della proprieta objectName Una volta preparato il file (che deve avere estensione .ts) si lancia il programma Qt Linguist e dal menù file si seleziona la voce Release, questo creerà un file .qm cheè il file compilato, il modello ricorda il giro file.po/file.gmo di gettext in effetti. QT Linguist ha un buon help in linea, fate riferimento a questo per tutti i problemi.
2 – Usare i file delle traduzioniUna volta creato il file .qm, il più è¨ fatto. La localizzazione dei programmi è gestita dalla classe QTranslator. Il codice necessario per inizializzare il tutto sono queste 3 righe:translator =QtCore.QTranslator()translator.load(QtCore.QString(‘i18n_it’))QtGui.qApp.installTranslator(translator)Dove il file (i18n_it in questo caso) puòessere completo di un path (relativo o assoluto)
3 – Preparare il codiceUna volta preparato il necessario e istanziata la classe, per avere la traduzione effettiva basta usare la funzione tr(“ciao”) per ogni stringa che si vuole tradurre.La funzione tr si preoccupa di recuperare la stringa tradotta in base al file della lingua caricato.Ci possono essere due risultati:
- la funzione trova la traduzione : viene usata
- la funzione non trova la traduzione: non succede nulla e viene usata la stringa passata come argomento
Piccola nota: se usate QtDesigner per creare l’interfaccia, il programma inserirà da solo tutte le chiamate alla funzione tr all’atto della generazione dei file necessari. A questo punto basta aggiungerli al file .ts e rilasciarlo.
-
Contrariamente alla versione precedente, con la versione 4 di Qt-Designer, non è possibile assegnare ad un evento uno slot custom, cosa che era molto comoda. Per ovviare a questo problema bisogna collegare un evento ad uno slot custom manualmente, dopo la creazione dell’interfaccia. Ecco un esempio.
class MainWin(Ui_DMainWin):
def __init__(self):
pass
def CustomSignal(self):
print “Hello”
app = QtGui.QApplication(sys.argv)
window = QtGui.QDialog()
ui = MainWin()
ui.setupUi(window)
#Custom Signal connections
QtCore.QObject.connect(ui.Button, QtCore.SIGNAL(“clicked()”), ui.CustomSignal)
-
La libreria Qt fornisce i dialoghi standard per la gestione di funzioni tipo Save, Open, Save As… e via dicendo. Però utilizzare i QFileDialog può essere un po’ ostico, soprattutto all’inizio. Ecco quindi un paio di cose da sapere. Filtri sui formati E’ possibile impostare una serie di filtri per i formati di file che sono gestiti, solo che non è proprio intuitivo o documentato chiaramente, almeno in PyQt4. I filtri per i formati file sono il 4 parametro (filter) dei metodi statici della classe QFileDialog e si deve specificare così: “html (*.html);;pdf (*.pdf);;All Supported Files (*.pdf *.html)” Formato selezionatoCome è possibile impostare l’elenco dei filtri, è anche possibile recuperare il tipo di file (filtro) selezionato dall’utente. Ovviamente anche qui, la cosa in PyQt è documentata male. Il sistema più comodo è questo:
fileType = QtCore.QString("html *.html")nfileName = QtGui.QFileDialog.getSaveFileName(None,
"SaveAs...",
".",
"html (*.html);;pdf (*.pdf);; All Supported Files (*.pdf *.html)",
fileType)
Dopo la chiusura del dialogo, fileType conterrà il filtro selezionato nella forma html (*.html)Attenzione al tipo del parametro fileType: deve essere di tipo QString, o la cosa non funziona.Il dialogo inoltre ritorna il nome del file con il percorso completo.
