ScolaSync  1.0
mainWindow.py
Aller à la documentation de ce fichier.
00001 #!/usr/bin/python
00002 # -*- coding: utf-8 -*-
00003 #       $Id: mainWindow.py 47 2011-06-13 10:20:14Z georgesk $   
00004 
00005 licence={}
00006 licence['en']="""
00007     file mainWindow.py
00008     this file is part of the project scolasync
00009     
00010     Copyright (C) 2010 Georges Khaznadar <georgesk@ofset.org>
00011 
00012     This program is free software: you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation, either version3 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00024 """
00025 
00026 from PyQt4.QtCore import *
00027 from PyQt4.QtGui import *
00028 import ownedUsbDisk, help, copyToDialog1, chooseInSticks, usbThread
00029 import diskFull, preferences
00030 import os.path, operator, subprocess, dbus, re, time, copy
00031 from notification import Notification
00032 import db
00033 from globaldef import logFileName
00034 
00035 # cette donnée est globale, pour être utilisé depuis n'importe quel objet
00036 qApp.diskData=ownedUsbDisk.Available(True,access="firstFat")
00037 
00038 ##
00039 # 
00040 #     Renvoie le premier répertoire existant d'une liste de propositions
00041 #     @param l la liste de propositions
00042 #     
00043 def firstdir(l):
00044     for d in l:
00045         if os.path.isdir(d): return d
00046     return None
00047    
00048 ##
00049 # 
00050 #     Renvoie le répertoire où trouver telle ou telle ressource
00051 #     @param which le type de ressource
00052 #     
00053 def _dir(which):
00054     if which=="lang":
00055         return firstdir(["/usr/share/scolasync/lang", "lang"])
00056     elif which=="help":
00057         return firstdir(["/usr/share/scolasync/help", "help"])
00058     elif which=="share":
00059         return firstdir(["/usr/share/scolasync/","share"])
00060     return None
00061 
00062 
00063 class mainWindow(QMainWindow):
00064     ##
00065     # 
00066     #         Le constructeur
00067     #         @param parent un QWidget
00068     #         @param opts une liste d'options extraite à l'aide de getopts
00069     #         @param locale la langue de l'application
00070     #         
00071     def __init__(self, parent, opts, locale="fr_FR"):
00072         QMainWindow.__init__(self)
00073         QWidget.__init__(self, parent)
00074         self.locale=locale
00075         from Ui_mainWindow  import Ui_MainWindow
00076         self.ui = Ui_MainWindow()
00077         self.ui.setupUi(self)
00078         self.t=self.ui.tableView
00079         self.opts=opts
00080         self.applyPreferences()
00081         self.timer=QTimer()
00082         self.updateButtons()
00083         self.oldThreads=set()
00084         self.flashTimer=QTimer()
00085         self.flashTimer.setSingleShot(True)
00086         QObject.connect(self.ui.forceCheckButton, SIGNAL("clicked()"), self.checkDisks)
00087         QObject.connect(self.timer, SIGNAL("timeout()"), self.checkDisks)
00088         QObject.connect(self.flashTimer, SIGNAL("timeout()"), self.normalLCD);
00089         QObject.connect(self.ui.helpButton, SIGNAL("clicked()"), self.help)
00090         QObject.connect(self.ui.umountButton, SIGNAL("clicked()"), self.umount)
00091         QObject.connect(self.ui.toButton, SIGNAL("clicked()"), self.copyTo)
00092         QObject.connect(self.ui.fromButton, SIGNAL("clicked()"), self.copyFrom)
00093         QObject.connect(self.ui.delButton, SIGNAL("clicked()"), self.delFiles)
00094         QObject.connect(self.ui.preferenceButton, SIGNAL("clicked()"), self.preference)
00095         QObject.connect(self.ui.tableView, SIGNAL("doubleClicked(const QModelIndex&)"), self.tableClicked)
00096 
00097     ##
00098     # 
00099     #         modification du comportement du widget original, pour
00100     #         démarrer le timer et les vérifications de baladeurs
00101     #         après construction de la fenêtre seulement
00102     #         
00103     def showEvent (self, ev):
00104         result=QMainWindow.showEvent(self, ev)
00105         self.timer.start(20000)
00106         self.checkDisks(force=True) # met à jour le compte de disques affiché
00107         return result
00108         
00109     ##
00110     # 
00111     #         Applique les préférences et les options de ligne de commande
00112     #         
00113     def applyPreferences(self):
00114         prefs=db.readPrefs()
00115         self.workdir=prefs["workdir"]
00116         # on active les cases à cocher si ça a été réclamé par les options
00117         # ou par les préférences
00118         self.checkable=("--check","") in self.opts or ("-c","") in self.opts or prefs["checkable"]
00119         other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00120         qApp.diskData=other
00121         self.header=ownedUsbDisk.uDisk.headers(self.checkable)
00122         self.connectTableModel(other)
00123         # met en ordre par la colonne des propriétaires
00124         self.t.setSortingEnabled(True)
00125         self.t.resizeColumnsToContents()
00126 
00127     ##
00128     # 
00129     #         change le répertoire par défaut contenant les fichiers de travail
00130     #         @param newDir le nouveau nom de répertoire
00131     #         
00132     def changeWd(self, newDir):
00133         self.workdir=newDir
00134         db.setWd(newDir)
00135 
00136     ##
00137     # 
00138     #         fonction de rappel pour un double clic sur un élément de la table
00139     #         @param idx un QModelIndex
00140     #         
00141     def tableClicked(self, idx):
00142         c=idx.column()
00143         r=idx.row()
00144         h=self.header[c]
00145         if c==0 and self.checkable:
00146             # case à cocher
00147             pass
00148         elif c==1:
00149             # case du propriétaire
00150             self.editOwner(r)
00151         elif "device-mount-paths" in h:
00152             cmd=u"nautilus '%s'" %idx.data().toString ()
00153             subprocess.call(cmd, shell=True)
00154         elif "device-size" in h:
00155             mount=idx.model().partition(idx).mountPoint()
00156             dev,total,used,remain,pcent,path = self.diskSizeData(mount)
00157             pcent=int(pcent[:-1])
00158             w=diskFull.mainWindow(self,pcent,title=path, total=total, used=used)
00159             w.show()
00160         else:
00161             QMessageBox.warning(None,
00162                                 QApplication.translate("Dialog","Double-clic non pris en compte",None, QApplication.UnicodeUTF8),
00163                                 QApplication.translate("Dialog","pas d'action pour l'attribut %1",None, QApplication.UnicodeUTF8).arg(h))
00164 
00165     ##
00166     # 
00167     #         @param rowOrDev a row number in the tableView, or a device string
00168     #         @return a tuple dev,total,used,remain,pcent,path for the
00169     #         disk in the given row of the tableView
00170     #         (the tuple comes from the command df)
00171     #         
00172     def diskSizeData(self, rowOrDev):
00173         if type(rowOrDev)==type(0):
00174             path=qApp.diskData[rowOrDev][self.header.index("1device-mount-paths")]
00175         else:
00176             path=rowOrDev
00177         cmd =u"df '%s'" %path
00178         dfOutput=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].split("\n")[-2]
00179         m = re.match("(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*", dfOutput).groups()
00180         return m
00181 
00182 
00183     ##
00184     # 
00185     #         trouve le disque qui correspond à une ligne du tableau
00186     #         @param ligne la ligne du tableau qui concerne une clé
00187     #         @return le disque correspondant à la ligne de tableau (type uDisk)
00188     #         
00189     def diskFromTableRow(self,ligne):
00190         stickid=qApp.diskData[ligne][self.header.index("5drive-serial")]
00191         found=False
00192         for d in qApp.diskData.disks.keys():
00193             if d.showableProp("drive-serial")==stickid:
00194                 found=True
00195                 break
00196         if found:
00197             return d
00198         else:
00199             return None
00200         
00201     ##
00202     # 
00203     #         Édition du propriétaire d'une clé.
00204     #         @param ligne la ligne du tableau qui concerne une clé
00205     #         
00206     def editOwner(self, ligne):
00207         student=qApp.diskData[ligne][1]
00208         ownedUsbDisk.editRecord(self.diskFromTableRow(ligne), student)
00209         other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00210         qApp.diskData=other
00211         self.connectTableModel(other)
00212         
00213     ##
00214     # 
00215     #         Désactive ou active les flèches selon que l'option correspondante
00216     #         est possible ou non. Pour les flèches : ça aurait du sens de préparer
00217     #         une opération de copie avant même de brancher des clés, donc on les
00218     #         active. Par contre démonter les clés quand elles sont absentes ça
00219     #         n'a pas d'utilité.
00220     #         
00221     def updateButtons(self):
00222         if len(qApp.diskData)>0:
00223             self.ui.toButton.setEnabled(True)
00224             self.ui.fromButton.setEnabled(True)
00225             self.ui.umountButton.setEnabled(True)
00226         else:
00227             self.ui.toButton.setEnabled(False)
00228             self.ui.fromButton.setEnabled(False)
00229             self.ui.umountButton.setEnabled(False)
00230 
00231     ##
00232     # 
00233     #         lance le dialogue des préférences
00234     #         
00235     def preference(self):
00236         pref=preferences.preferenceWindow()
00237         pref.setValues(db.readPrefs())
00238         pref.show()
00239         pref.exec_()
00240         if pref.result()==QDialog.Accepted:
00241             db.writePrefs(pref.values())
00242             # on applique les préférences tout de suite sans redémarrer
00243             self.applyPreferences()
00244             
00245     ##
00246     # 
00247     #         Lance l'action de supprimer des fichiers ou des répertoires dans les clés USB
00248     #         
00249     def delFiles(self):
00250         titre1=QApplication.translate("Dialog","Choix de fichiers à supprimer",None, QApplication.UnicodeUTF8)
00251         titre2=QApplication.translate("Dialog","Choix de fichiers à supprimer (jokers autorisés)",None, QApplication.UnicodeUTF8)
00252         d=chooseInSticks.chooseDialog(self, titre1, titre2)
00253         ok = d.exec_()
00254         if ok:
00255             pathList=map(lambda x: u"%s" %x, d.pathList())
00256             reply=QMessageBox.critical(None,
00257                                        QApplication.translate("Dialog","Vous allez effacer plusieurs baladeurs",None, QApplication.UnicodeUTF8),
00258                                        QApplication.translate("Dialog","Etes-vous certain de vouloir effacer : "+",".join(pathList),None, QApplication.UnicodeUTF8))
00259             if reply == QMessageBox.Ok:
00260                 for p in qApp.diskData:
00261                     t=usbThread.threadDeleteInUSB(p,pathList,subdir="Travail", logfile=logFileName)
00262                     t.setDaemon(True)
00263                     t.start()
00264                 self.checkDisks() # met à jour les symboles d'activité
00265         else:
00266             msgBox=QMessageBox.warning(None,
00267                                        QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00268                                        QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00269 
00270     ##
00271     # 
00272     #         Lance l'action de copier vers les clés USB
00273     #         
00274     def copyTo(self):
00275         d=copyToDialog1.copyToDialog1(parent=self, workdir=self.workdir)
00276         d.exec_()
00277         if d.ok==True:
00278             for p in qApp.diskData:
00279                 subdir=self.workdir
00280                 t=usbThread.threadCopyToUSB(p,d.selectedList(),subdir=subdir, logfile=logFileName)
00281                 t.setDaemon(True)
00282                 t.start()
00283             self.checkDisks() # met à jour les symboles d'activité
00284         else:
00285             msgBox=QMessageBox.warning(None,
00286                                        QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00287                                        QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00288 
00289     ##
00290     # 
00291     #         Lance l'action de copier depuis les clés USB
00292     #         
00293     def copyFrom(self):
00294         titre1=QApplication.translate("Dialog","Choix de fichiers à copier",None, QApplication.UnicodeUTF8)
00295         titre2=QApplication.translate("Dialog", "Choix de fichiers à copier depuis les baladeurs", None, QApplication.UnicodeUTF8)
00296         ok=QApplication.translate("Dialog", "Choix de la destination ...", None, QApplication.UnicodeUTF8)
00297         d=chooseInSticks.chooseDialog(self, title1=titre1, title2=titre2, ok=ok)
00298         ok = d.exec_()
00299         if not ok or len(d.pathList())==0 :
00300             msgBox=QMessageBox.warning(None,
00301                                        QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00302                                        QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00303             return
00304         # bon, alors c'est OK pour le choix des fichiers à envoyer
00305         pathList=map(lambda x: u"%s" %x, d.pathList())
00306         mp=d.selectedDiskMountPoint()
00307         initialPath=os.path.expanduser("~")
00308         destDir = QFileDialog.getExistingDirectory(None,
00309                                                    QApplication.translate("Dialog","Choisir un répertoire de destination",None, QApplication.UnicodeUTF8),
00310                                                    initialPath)
00311         if destDir and len(destDir)>0 :
00312             dest=u"%s" %destDir
00313             for p in qApp.diskData:
00314                 # on devrait vérifier s'il y a des données à copier
00315                 # et s'il n'y en a pas, ajouter des lignes au journal
00316                 # mais on va laisser faire ça dans le thread
00317                 # inconvénient : ça crée quelquefois des sous-répertoires
00318                 # vides inutiles dans le répertoire de destination.
00319                 t=usbThread.threadCopyFromUSB(p,pathList,subdir=self.workdir,
00320                                               rootPath=mp,
00321                                               dest=dest,
00322                                               logfile=logFileName)
00323                 t.setDaemon(True)
00324                 t.start()
00325             self.checkDisks() # met à jour les symboles d'activité
00326             # on ouvre nautilus pour voir le résultat des copies
00327             if QMessageBox.question(None,
00328                                     QApplication.translate("Dialog","Voir les copies",None, QApplication.UnicodeUTF8),
00329                                     QApplication.translate("Dialog","Voulez-vous voir les fichiers copiés ?",None, QApplication.UnicodeUTF8)):
00330                 subprocess.call("nautilus '%s'" %dest,shell=True)
00331         else:
00332             msgBox=QMessageBox.warning(None,
00333                                        QApplication.translate("Dialog","Destination manquante",None, QApplication.UnicodeUTF8),
00334                                        QApplication.translate("Dialog","Veuillez choisir une destination pour la copie des fichiers",None, QApplication.UnicodeUTF8))
00335 
00336     
00337 
00338     ##
00339     # 
00340     #         Affiche le widget d'aide
00341     #         
00342     def help(self):
00343         w=help.helpWindow(None)
00344         w.loadBrowsers(_dir("help"),self.locale)
00345         w.show()
00346         w.exec_()
00347 
00348     ##
00349     # 
00350     #         Démonte et détache les clés USB affichées
00351     #         
00352     def umount(self):
00353         buttons=QMessageBox.StandardButtons(QMessageBox.Ok+QMessageBox.Cancel)
00354         button=QMessageBox.critical ( self,
00355                                       QApplication.translate("Main","Démontage des baladeurs",None, QApplication.UnicodeUTF8),
00356                                       QApplication.translate("Main","Êtes-vous sûr de vouloir démonter tous les baladeurs cochés de la liste ?",None, QApplication.UnicodeUTF8),
00357                                       buttons=buttons)
00358         if button!=QMessageBox.Ok:
00359             return
00360         # on parcourt les premières partition FAT
00361         for p in qApp.diskData:
00362             # on trouve leurs disques parents
00363             for d in qApp.diskData.disks.keys():
00364                 if p in qApp.diskData.disks[d] and p.selected:
00365                     # démontage de toutes les partitions du même disque parent
00366                     for partition in qApp.diskData.disks[d]:
00367                         devfile=partition.getProp("device-file-by-id")
00368                         if isinstance(devfile, dbus.Array):
00369                             devfile=devfile[0]
00370                         subprocess.call("udisks --unmount %s" %devfile, shell=True)
00371                     # détachement du disque parent
00372                     devfile_disk=d.getProp("device-file-by-id")
00373                     if isinstance(devfile_disk, dbus.Array):
00374                         devfile_disk=devfile_disk[0]
00375                     subprocess.call("udisks --detach %s" %devfile_disk, shell=True)
00376                     break
00377         self.checkDisks() # remet à jour le compte de disques
00378                 
00379 
00380     ##
00381     # 
00382     #         Connecte le modèle de table à la table
00383     #         @param data les données de la table
00384     #         
00385     def connectTableModel(self, data):
00386         self.visibleheader=[]
00387         for h in self.header:
00388             if h in ownedUsbDisk.uDisk._itemNames:
00389                 self.visibleheader.append(self.tr(ownedUsbDisk.uDisk._itemNames[h]))
00390             else:
00391                 self.visibleheader.append(h)
00392         self.tm=usbTableModel(self, self.visibleheader,data,self.checkable)
00393         self.t.setModel(self.tm)
00394         if self.checkable:
00395             self.t.setItemDelegateForColumn(0, CheckBoxDelegate(self))
00396             self.t.setItemDelegateForColumn(1, UsbDiskDelegate(self))
00397             self.t.setItemDelegateForColumn(3, DiskSizeDelegate(self))
00398         else:
00399             self.t.setItemDelegateForColumn(0, UsbDiskDelegate(self))
00400             self.t.setItemDelegateForColumn(2, DiskSizeDelegate(self))
00401         
00402     ##
00403     # 
00404     #         fonction relancée périodiquement pour vérifier s'il y a un changement
00405     #         dans le baladeurs, et signaler dans le tableau les threads en cours.
00406     #         Le tableau est complètement régénéré à chaque fois, ce qui n'est pas
00407     #         toujours souhaitable.
00408     #         À la fin de chaque vérification, un court flash est déclenché sur
00409     #         l'afficheur de nombre de baladeurs connectés et sa valeur est mise à
00410     #         jour.
00411     #         @param force pour forcer une mise à jour du tableau
00412     #         
00413     def checkDisks(self, force=False):
00414         other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00415         if force or not self.sameDiskData(qApp.diskData, other):
00416             qApp.diskData=other
00417             self.oldThreads=copy.copy(usbThread.globalThreads.threadSet())
00418             connectedCount=int(other)
00419             self.connectTableModel(other)
00420             self.updateButtons()
00421             self.t.resizeColumnsToContents()
00422             self.ui.lcdNumber.display(connectedCount)
00423         self.flashLCD()
00424         self.t.sortByColumn(2)
00425 
00426     ##
00427     # 
00428     #         @return True si les ensembles de uniqueId de one et two sont identiques, et si les informations sur les threads n'ont pas varié
00429     #         
00430     def sameDiskData(self, one, two):
00431         return set([p.uniqueId() for p in one]) == set([p.uniqueId() for p in two]) and self.oldThreads == usbThread.globalThreads.threadSet()
00432 
00433     ##
00434     # 
00435     #         change le style de l'afficheur LCD pendant une fraction de seconde
00436     #         
00437     def flashLCD(self):
00438         self.ui.lcdNumber.setBackgroundRole(QPalette.Highlight)
00439         self.flashTimer.start(250) ## un quart de seconde
00440 
00441     ##
00442     # 
00443     #         remet le style par défaut pour l'afficheur LCD
00444     #         
00445     def normalLCD(self):
00446         self.ui.lcdNumber.setBackgroundRole(QPalette.Window)
00447 
00448 ##
00449 # 
00450 #     Un modèle de table pour des séries de clés USB
00451 #     
00452 class usbTableModel(QAbstractTableModel):
00453 
00454     ##
00455     # 
00456     #         @param parent un QObject
00457     #         @param header les en-têtes de colonnes
00458     #         @param donnees les données
00459     #         @param checkable vrai si la première colonne est composée de boîtes à cocher. Faux par défaut
00460     #         
00461     def __init__(self, parent=None, header=[], donnees=None, checkable=False):
00462         QAbstractTableModel.__init__(self,parent)
00463         self.header=header
00464         self.donnees=donnees
00465         self.checkable=checkable
00466         self.pere=parent
00467 
00468     ##
00469     # 
00470     #         @parent un QModelIndex
00471     #         
00472     def rowCount(self, parent):
00473         return len(self.donnees)
00474     
00475     ##
00476     # 
00477     #         @parent un QModelIndex
00478     #         
00479     def columnCount(self, parent): 
00480         return len(self.header) 
00481 
00482     def setData(self, index, value, role):
00483         if index.column()==0 and self.checkable:
00484             self.donnees[index.row()].selected=value
00485             return True
00486         else:
00487             return QAbstractTableModel.setData(self, index, role)
00488 
00489     ##
00490     # 
00491     #         @param index in QModelIndex
00492     #         @return la partition pointée par index
00493     #         
00494     def partition(self, index):
00495         return self.donnees[index.row()][-1]
00496         
00497     def data(self, index, role): 
00498         if not index.isValid(): 
00499             return QVariant()
00500         elif role==Qt.ToolTipRole:
00501             c=index.column()
00502             h=self.pere.header[c]
00503             if c==0 and self.checkable:
00504                 return QApplication.translate("Main","Cocher ou décocher cette case en cliquant.",None, QApplication.UnicodeUTF8)
00505             elif c==1:
00506                 return QApplication.translate("Main","Propriétaire de la clé USB ou du baladeur ;<br><b>Double-clic</b> pour modifier.",None, QApplication.UnicodeUTF8)
00507             elif "device-mount-paths" in h:
00508                 return QApplication.translate("Main","Point de montage de la clé USB ou du baladeur ;<br><b>Double-clic</b> pour voir les fichiers.",None, QApplication.UnicodeUTF8)
00509             elif "device-size" in h:
00510                 return QApplication.translate("Main","Capacité de la clé USB ou du baladeur en kO ;<br><b>Double-clic</b> pour voir la place occupée.",None, QApplication.UnicodeUTF8)
00511             elif "drive-vendor" in h:
00512                 return QApplication.translate("Main","Fabricant de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00513             elif "drive-model" in h:
00514                 return QApplication.translate("Main","Modèle de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00515             elif "drive-serial" in h:
00516                 return QApplication.translate("Main","Numéro de série de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00517             else:
00518                 return ""
00519         elif role != Qt.DisplayRole: 
00520             return QVariant()
00521         if index.row()<len(self.donnees):
00522             return QVariant(self.donnees[index.row()][index.column()])
00523         else:
00524             return QVariant()
00525 
00526     def headerData(self, section, orientation, role):
00527         if orientation == Qt.Horizontal and role == Qt.DisplayRole:
00528             return QVariant(self.header[section])
00529         elif orientation == Qt.Vertical and role == Qt.DisplayRole:
00530             return QVariant(section+1)
00531         return QVariant()
00532 
00533     ##
00534     # Sort table by given column number.
00535     #         @param Ncol numéro de la colonne de tri
00536     #         @param order l'odre de tri, Qt.DescendingOrder par défaut
00537     #         
00538     def sort(self, Ncol, order=Qt.DescendingOrder):
00539         self.emit(SIGNAL("layoutAboutToBeChanged()"))
00540         self.donnees = sorted(self.donnees, key=operator.itemgetter(Ncol))        
00541         if order == Qt.DescendingOrder:
00542             self.donnees.reverse()
00543         self.emit(SIGNAL("layoutChanged()"))
00544 
00545 def CheckBoxRect(view_item_style_options):
00546   check_box_style_option=QStyleOptionButton()
00547   check_box_rect = QApplication.style().subElementRect(QStyle.SE_CheckBoxIndicator,check_box_style_option)
00548   check_box_point=QPoint(view_item_style_options.rect.x() + view_item_style_options.rect.width() / 2 - check_box_rect.width() / 2, view_item_style_options.rect.y() + view_item_style_options.rect.height() / 2 - check_box_rect.height() / 2)
00549   return QRect(check_box_point, check_box_rect.size())
00550 
00551 class CheckBoxDelegate(QStyledItemDelegate):
00552     def __init__(self, parent):
00553         QStyledItemDelegate.__init__(self,parent)
00554 
00555     def paint(self, painter, option, index):
00556         checked = index.model().data(index, Qt.DisplayRole).toBool()
00557         check_box_style_option=QStyleOptionButton()
00558         check_box_style_option.state |= QStyle.State_Enabled
00559         if checked:
00560             check_box_style_option.state |= QStyle.State_On
00561         else:
00562             check_box_style_option.state |= QStyle.State_Off
00563         check_box_style_option.rect = CheckBoxRect(option);
00564         QApplication.style().drawControl(QStyle.CE_CheckBox, check_box_style_option, painter)
00565 
00566     def editorEvent(self, event, model, option, index):
00567         if ((event.type() == QEvent.MouseButtonRelease) or (event.type() == QEvent.MouseButtonDblClick)):
00568             if (event.button() != Qt.LeftButton or not CheckBoxRect(option).contains(event.pos())):
00569                 return False
00570             if (event.type() == QEvent.MouseButtonDblClick):
00571                 return True
00572         elif (event.type() == QEvent.KeyPress):
00573             if event.key() != Qt.Key_Space and event.key() != Qt.Key_Select:
00574                 return False
00575         else:
00576             return False
00577         checked = index.model().data(index, Qt.DisplayRole).toBool()
00578         result = model.setData(index, not checked, Qt.EditRole)
00579         return result
00580 
00581         
00582 class UsbDiskDelegate(QStyledItemDelegate):
00583     def __init__(self, parent):
00584         QStyledItemDelegate.__init__(self,parent)
00585         self.okPixmap=QPixmap("/usr/share/icons/Tango/16x16/status/weather-clear.png")
00586         self.busyPixmap=QPixmap("/usr/share/icons/Tango/16x16/actions/view-refresh.png")
00587 
00588     def paint(self, painter, option, index):
00589         text = index.model().data(index, Qt.DisplayRole).toString()
00590         rect0=QRect(option.rect)
00591         rect1=QRect(option.rect)
00592         h=rect0.height()
00593         w=rect0.width()
00594         rect0.setSize(QSize(h,h))
00595         rect1.translate(h,0)
00596         rect1.setSize(QSize(w-h,h))
00597         QApplication.style().drawItemText (painter, rect1, Qt.AlignLeft+Qt.AlignVCenter, option.palette, True, text)
00598         QApplication.style().drawItemText (painter, rect0, Qt.AlignCenter, option.palette, True, QString("O"))
00599         if usbThread.globalThreads.busy(u"%s" %text):
00600             QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.busyPixmap)
00601         else:
00602             QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.okPixmap)
00603         
00604 class DiskSizeDelegate(QStyledItemDelegate):
00605     def __init__(self, parent):
00606         QStyledItemDelegate.__init__(self,parent)
00607         
00608 
00609     def paint(self, painter, option, index):
00610         value = int(index.model().data(index, Qt.DisplayRole).toString())
00611         text = self.val2txt(value)
00612         rect0=QRect(option.rect)
00613         rect1=QRect(option.rect)
00614         rect0.translate(2,(rect0.height()-16)/2)
00615         rect0.setSize(QSize(16,16))
00616         rect1.translate(20,0)
00617         rect1.setWidth(rect1.width()-20)
00618         QApplication.style().drawItemText (painter, rect1, Qt.AlignLeft+Qt.AlignVCenter, option.palette, True, text)
00619         # dessin d'un petit cercle pour l'occupation
00620         mount=index.model().partition(index).mountPoint()
00621         dev,total,used,remain,pcent,path = self.parent().diskSizeData(mount)
00622         pcent=int(pcent[:-1])
00623         painter.setBrush(QBrush(QColor("slateblue")))
00624         painter.drawPie(rect0,0,16*360*pcent/100)
00625 
00626     ##
00627     # 
00628     #         @return a string with a value with unit K, M, or G
00629     #         
00630     def val2txt(self, val):
00631         suffixes=["B", "KB", "MB", "GB", "TB"]
00632         val*=1.0 # calcul flottant
00633         i=0
00634         while val > 1024 and i < len(suffixes):
00635             i+=1
00636             val/=1024
00637         return "%4.1f %s" %(val, suffixes[i])
00638     
00639 
 Tout Classes Espaces de nommage Fichiers Fonctions Variables