00001
00002
00003
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, checkBoxDialog
00030 import os.path, operator, subprocess, dbus, re, time, copy
00031 from notification import Notification
00032 import db
00033 import deviceListener
00034 import choixEleves
00035 import nameAdrive
00036 from globaldef import logFileName, _dir
00037
00038
00039 qApp.diskData=ownedUsbDisk.Available(True,access="firstFat")
00040
00041 activeThreads={}
00042
00043
00044 pastCommands={}
00045 lastCommand=None
00046
00047
00048
00049
00050
00051
00052
00053 def registerCmd(cmd,partition):
00054 global pastCommands, lastCommand
00055 if pastCommands.has_key(cmd):
00056 pastCommands[cmd].append(partition.owner)
00057 else:
00058 pastCommands[cmd]=[partition.owner]
00059 lastCommand=cmd
00060
00061 class mainWindow(QMainWindow):
00062
00063
00064
00065
00066
00067
00068
00069 def __init__(self, parent, opts, locale="fr_FR"):
00070 QMainWindow.__init__(self)
00071 QWidget.__init__(self, parent)
00072 self.locale=locale
00073 from Ui_mainWindow import Ui_MainWindow
00074 self.ui = Ui_MainWindow()
00075 self.ui.setupUi(self)
00076
00077 self.namesFullIcon=QIcon("/usr/share/icons/Tango/scalable/actions/gtk-find-and-replace.svg")
00078 self.namesEmptyIcon=QIcon("/usr/share/icons/Tango/scalable/actions/gtk-find.svg")
00079 self.namesFullTip=QApplication.translate("MainWindow", "<br />Des noms sont disponibles pour renommer les prochains baladeurs que vous brancherez", None, QApplication.UnicodeUTF8)
00080 self.namesEmptyTip=QApplication.translate("MainWindow", "<br />Cliquez sur ce bouton pour préparer une liste de noms afin de renommer les prochains baladeurs que vous brancherez", None, QApplication.UnicodeUTF8)
00081 self.namesDialog=choixEleves.choixElevesDialog(parent =self)
00082 self.recentConnect=""
00083
00084 self.initRedoStuff()
00085
00086 self.t=self.ui.tableView
00087 self.proxy=QSortFilterProxyModel()
00088 self.proxy.setSourceModel(self.t.model())
00089 self.opts=opts
00090 self.timer=QTimer()
00091 self.applyPreferences()
00092 self.listener=deviceListener.DeviceListener(self)
00093 self.updateButtons()
00094 self.availableNames=False
00095 self.setAvailableNames(False)
00096 self.operations=[]
00097 self.oldThreads=set()
00098 self.flashTimer=QTimer()
00099 self.flashTimer.setSingleShot(True)
00100 self.checkDisksLock=False
00101 QObject.connect(self.ui.forceCheckButton, SIGNAL("clicked()"), self.checkDisks)
00102 QObject.connect(self.timer, SIGNAL("timeout()"), self.checkDisks)
00103 QObject.connect(self.flashTimer, SIGNAL("timeout()"), self.normalLCD);
00104 QObject.connect(self.ui.helpButton, SIGNAL("clicked()"), self.help)
00105 QObject.connect(self.ui.umountButton, SIGNAL("clicked()"), self.umount)
00106 QObject.connect(self.ui.toButton, SIGNAL("clicked()"), self.copyTo)
00107 QObject.connect(self.ui.fromButton, SIGNAL("clicked()"), self.copyFrom)
00108 QObject.connect(self.ui.delButton, SIGNAL("clicked()"), self.delFiles)
00109 QObject.connect(self.ui.redoButton, SIGNAL("clicked()"), self.redoCmd)
00110 QObject.connect(self.ui.namesButton, SIGNAL("clicked()"), self.namesCmd)
00111 QObject.connect(self.ui.preferenceButton, SIGNAL("clicked()"), self.preference)
00112 QObject.connect(self.ui.tableView, SIGNAL("doubleClicked(const QModelIndex&)"), self.tableClicked)
00113 QObject.connect(self,SIGNAL("deviceAdded(QString)"), self.deviceAdded)
00114 QObject.connect(self,SIGNAL("deviceRemoved(QString)"), self.deviceRemoved)
00115 QObject.connect(self,SIGNAL("checkAll()"), self.checkAll)
00116 QObject.connect(self,SIGNAL("checkToggle()"), self.checkToggle)
00117 QObject.connect(self,SIGNAL("checkNone()"), self.checkNone)
00118 QObject.connect(self,SIGNAL("shouldNameDrive()"), self.namingADrive)
00119
00120
00121
00122
00123
00124
00125
00126 def checkModify(self, boolFunc):
00127 model=self.tm
00128 index0=model.createIndex(0,0)
00129 index1=model.createIndex(len(model.donnees)-1,0)
00130 srange=QItemSelectionRange(index0,index1)
00131 for i in srange.indexes():
00132 checked=i.model().data(i,Qt.DisplayRole).toBool()
00133 model.setData(i, boolFunc(checked),Qt.EditRole)
00134
00135
00136
00137
00138
00139 def checkAll(self):
00140 self.checkModify(lambda x: True)
00141
00142
00143
00144
00145
00146 def checkToggle(self):
00147 self.checkModify(lambda x: not x)
00148
00149
00150
00151
00152
00153 def checkNone(self):
00154 self.checkModify(lambda x: False)
00155
00156
00157
00158
00159
00160
00161 def namingADrive(self):
00162 if self.availableNames:
00163 stickId, tattoo, uuid = self.listener.identify(self.recentConnect)
00164 hint=db.readStudent(stickId, uuid, tattoo)
00165 if hint != None:
00166 oldName=hint
00167 else:
00168 oldName=""
00169 d=nameAdrive.nameAdriveDialog(self, oldName=oldName,
00170 nameList=self.namesDialog.itemStrings(),
00171 driveIdent=(stickId, uuid, tattoo))
00172 d.show()
00173 result=d.exec_()
00174 return
00175
00176
00177
00178
00179
00180
00181 def deviceAdded(self, s):
00182 vfatPath = self.listener.vfatUsbPath(str(s))
00183 if vfatPath:
00184 self.recentConnect=str(s)
00185
00186
00187 QTimer.singleShot(0, self.namingADrive)
00188 self.checkDisks(noLoop=True)
00189
00190
00191
00192
00193
00194
00195 def deviceRemoved(self, s):
00196
00197 if qApp.diskData.hasDev(s):
00198 self.checkDisks()
00199
00200
00201
00202
00203
00204 def initRedoStuff(self):
00205
00206 self.iconRedo = QIcon()
00207 self.iconRedo.addPixmap(QPixmap("/usr/share/icons/Tango/scalable/actions/go-jump.svg"), QIcon.Normal, QIcon.Off)
00208 self.iconStop = QIcon()
00209 self.iconStop.addPixmap(QPixmap("/usr/share/icons/Tango/scalable/actions/stop.svg"), QIcon.Normal, QIcon.Off)
00210
00211 self.redoToolTip=QApplication.translate("MainWindow", "Refaire à nouveau", None, QApplication.UnicodeUTF8)
00212 self.redoStatusTip=QApplication.translate("MainWindow", "Refaire à nouveau la dernière opération réussie, avec les baladeurs connectés plus récemment", None, QApplication.UnicodeUTF8)
00213 self.stopToolTip=QApplication.translate("MainWindow", "Arrêter les opérations en cours", None, QApplication.UnicodeUTF8)
00214 self.stopStatusTip=QApplication.translate("MainWindow", "Essaie d'arrêter les opérations en cours. À faire seulement si celles-ci durent trop longtemps", None, QApplication.UnicodeUTF8)
00215
00216
00217
00218
00219
00220
00221
00222 def showEvent (self, ev):
00223 result=QMainWindow.showEvent(self, ev)
00224 self.setTimer()
00225 self.checkDisks(force=True)
00226 return result
00227
00228
00229
00230
00231
00232 def setTimer(self, enabled=True):
00233 if self.refreshEnabled:
00234 self.timer.start(self.refreshDelay*1000)
00235 else:
00236 self.timer.stop()
00237
00238
00239
00240
00241
00242 def applyPreferences(self):
00243 prefs=db.readPrefs()
00244 self.schoolFile=prefs["schoolFile"]
00245 self.workdir=prefs["workdir"]
00246 self.refreshEnabled=prefs["refreshEnabled"]
00247 self.refreshDelay=prefs["refreshDelay"]
00248 self.setTimer()
00249 self.manFileLocation=prefs["manfile"]
00250
00251
00252 self.checkable=("--check","") in self.opts or ("-c","") in self.opts or prefs["checkable"]
00253 self.mv=prefs["mv"]
00254 other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00255 qApp.diskData=other
00256 self.header=ownedUsbDisk.uDisk.headers(self.checkable)
00257 self.connectTableModel(other)
00258
00259
00260
00261
00262
00263
00264 def changeWd(self, newDir):
00265 self.workdir=newDir
00266 db.setWd(newDir)
00267
00268
00269
00270
00271
00272
00273 def tableClicked(self, idx):
00274 c=idx.column()
00275 mappedIdx=self.proxy.mapFromSource(idx)
00276 r=mappedIdx.row()
00277
00278 h=self.header[c]
00279 if c==0 and self.checkable:
00280 self.manageCheckBoxes()
00281 pass
00282 elif c==1 or (c==0 and not self.checkable):
00283
00284 self.editOwner(mappedIdx)
00285 elif "device-mount-paths" in h:
00286 cmd=u"nautilus '%s'" %idx.data().toString ()
00287 subprocess.call(cmd, shell=True)
00288 elif "device-size" in h:
00289 mount=idx.model().partition(idx).mountPoint()
00290 dev,total,used,remain,pcent,path = self.diskSizeData(mount)
00291 pcent=int(pcent[:-1])
00292 w=diskFull.mainWindow(self,pcent,title=path, total=total, used=used)
00293 w.show()
00294 else:
00295 QMessageBox.warning(None,
00296 QApplication.translate("Dialog","Double-clic non pris en compte",None, QApplication.UnicodeUTF8),
00297 QApplication.translate("Dialog","pas d'action pour l'attribut %1",None, QApplication.UnicodeUTF8).arg(h))
00298
00299
00300
00301
00302
00303 def manageCheckBoxes(self):
00304 cbDialog=checkBoxDialog.CheckBoxDialog(self)
00305 cbDialog.exec_()
00306
00307
00308
00309
00310
00311
00312
00313
00314 def diskSizeData(self, rowOrDev):
00315 if type(rowOrDev)==type(0):
00316 path=qApp.diskData[rowOrDev][self.header.index("1device-mount-paths")]
00317 else:
00318 path=rowOrDev
00319 cmd =u"df '%s'" %path
00320 dfOutput=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].split("\n")[-2]
00321 m = re.match("(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*", dfOutput).groups()
00322 return m
00323
00324
00325
00326
00327
00328
00329
00330
00331 def diskFromOwner(self,student):
00332 found=False
00333 for d in qApp.diskData.disks.keys():
00334 if d.owner==student:
00335 found=True
00336 break
00337
00338
00339 if d.owner==None or len(d.owner)==0:
00340 found=True
00341 break
00342 if found:
00343 return d
00344 else:
00345 return None
00346
00347
00348
00349
00350
00351
00352 def editOwner(self, idx):
00353 student=u"%s" %self.tm.data(idx,Qt.DisplayRole).toString()
00354 ownedUsbDisk.editRecord(self.diskFromOwner(student), hint=student)
00355 other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00356 qApp.diskData=other
00357 self.connectTableModel(other)
00358 self.checkDisks()
00359
00360
00361
00362
00363
00364
00365
00366
00367 def setAvailableNames(self, available):
00368 self.availableNames=available
00369 if available:
00370 icon=self.namesFullIcon
00371 msg=self.namesFullTip
00372 else:
00373 icon=self.namesEmptyIcon
00374 msg=self.namesEmptyTip
00375 self.ui.namesButton.setIcon(icon)
00376 self.ui.namesButton.setToolTip(msg)
00377 self.ui.namesButton.setStatusTip(msg.remove("<br />"))
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389 def updateButtons(self):
00390 global activeThreads, lastCommand
00391 active = len(qApp.diskData)>0
00392 for button in (self.ui.toButton,
00393 self.ui.fromButton,
00394 self.ui.delButton,
00395 self.ui.umountButton):
00396 button.setEnabled(active)
00397
00398
00399
00400
00401 if len(activeThreads) > 0:
00402 self.ui.redoButton.setIcon(self.iconStop)
00403 self.ui.redoButton.setToolTip(self.stopToolTip)
00404 self.ui.redoButton.setStatusTip(self.stopStatusTip)
00405 self.ui.redoButton.setEnabled(True)
00406 else:
00407 self.oldThreads=set()
00408 self.ui.redoButton.setIcon(self.iconRedo)
00409 self.ui.redoButton.setToolTip(self.redoToolTip)
00410 self.ui.redoButton.setStatusTip(self.redoStatusTip)
00411 self.ui.redoButton.setEnabled(lastCommand!=None)
00412 l=self.namesDialog.ui.listWidget.findItems("*",Qt.MatchWildcard)
00413 if len(l)>0:
00414 self.ui.namesButton.setIcon(self.namesFullIcon)
00415 else:
00416 self.ui.namesButton.setIcon(self.namesEmptyIcon)
00417
00418
00419
00420
00421
00422 def preference(self):
00423 pref=preferences.preferenceWindow()
00424 pref.setValues(db.readPrefs())
00425 pref.show()
00426 pref.exec_()
00427 if pref.result()==QDialog.Accepted:
00428 db.writePrefs(pref.values())
00429
00430 self.applyPreferences()
00431
00432
00433
00434
00435
00436 def delFiles(self):
00437 titre1=QApplication.translate("Dialog","Choix de fichiers à supprimer",None, QApplication.UnicodeUTF8)
00438 titre2=QApplication.translate("Dialog","Choix de fichiers à supprimer (jokers autorisés)",None, QApplication.UnicodeUTF8)
00439 d=chooseInSticks.chooseDialog(self, titre1, titre2)
00440 ok = d.exec_()
00441 if ok:
00442 pathList=map(lambda x: u"%s" %x, d.pathList())
00443 buttons=QMessageBox.Ok|QMessageBox.Cancel
00444 defaultButton=QMessageBox.Cancel
00445 reply=QMessageBox.warning(
00446 None,
00447 QApplication.translate("Dialog","Vous allez effacer plusieurs baladeurs",None, QApplication.UnicodeUTF8),
00448 QApplication.translate("Dialog","Etes-vous certain de vouloir effacer : "+"\n".join(pathList),None, QApplication.UnicodeUTF8),
00449 buttons, defaultButton)
00450 if reply == QMessageBox.Ok:
00451 cmd='t=usbThread.threadDeleteInUSB(p,%s,subdir="Travail", logfile="%s", parent=self.tm)' %(pathList,logFileName)
00452 for p in qApp.diskData:
00453 if not p.selected: continue
00454 registerCmd(cmd,p)
00455 exec(compile(cmd,'<string>','exec'))
00456 t.setDaemon(True)
00457 t.start()
00458 self.oldThreads.add(t)
00459 return True
00460 else:
00461 msgBox=QMessageBox.warning(
00462 None,
00463 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00464 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00465 return True
00466
00467
00468
00469
00470
00471 def copyTo(self):
00472 d=copyToDialog1.copyToDialog1(parent=self, workdir=self.workdir)
00473 d.exec_()
00474 if d.ok==True:
00475 cmd='t=usbThread.threadCopyToUSB(p,%s,subdir="%s", logfile="%s", parent=self.tm)' %(d.selectedList(), self.workdir, logFileName)
00476 for p in qApp.diskData:
00477 if not p.selected: continue
00478 registerCmd(cmd,p)
00479 exec(compile(cmd,'<string>','exec'))
00480 t.setDaemon(True)
00481 t.start()
00482 self.oldThreads.add(t)
00483 return True
00484 else:
00485 msgBox=QMessageBox.warning(
00486 None,
00487 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00488 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00489 return True
00490
00491
00492
00493
00494
00495 def copyFrom(self):
00496 titre1=QApplication.translate("Dialog","Choix de fichiers à copier",None, QApplication.UnicodeUTF8)
00497 titre2=QApplication.translate("Dialog", "Choix de fichiers à copier depuis les baladeurs", None, QApplication.UnicodeUTF8)
00498 ok=QApplication.translate("Dialog", "Choix de la destination ...", None, QApplication.UnicodeUTF8)
00499 d=chooseInSticks.chooseDialog(self, title1=titre1, title2=titre2, ok=ok)
00500 ok = d.exec_()
00501 if not ok or len(d.pathList())==0 :
00502 msgBox=QMessageBox.warning(None,
00503 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00504 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00505 return True
00506
00507 pathList=map(lambda x: u"%s" %x, d.pathList())
00508 mp=d.selectedDiskMountPoint()
00509 initialPath=os.path.expanduser("~")
00510 destDir = QFileDialog.getExistingDirectory(
00511 None,
00512 QApplication.translate("Dialog","Choisir un répertoire de destination",None, QApplication.UnicodeUTF8),
00513 initialPath)
00514 if destDir and len(destDir)>0 :
00515 if self.mv:
00516 cmd=u"""t=usbThread.threadMoveFromUSB(
00517 p,%s,subdir=self.workdir,
00518 rootPath="%s", dest="%s", logfile="%s",
00519 parent=self.tm)""" %(pathList, mp, destDir, logFileName)
00520 else:
00521 cmd=u"""t=usbThread.threadCopyFromUSB(
00522 p,%s,subdir=self.workdir,
00523 rootPath="%s", dest="%s", logfile="%s",
00524 parent=self.tm)""" %(pathList, mp, destDir, logFileName)
00525
00526 for p in qApp.diskData:
00527 if not p.selected: continue
00528
00529
00530
00531
00532
00533 registerCmd(cmd,p)
00534 exec(compile(cmd,'<string>','exec'))
00535 t.setDaemon(True)
00536 t.start()
00537 self.oldThreads.add(t)
00538
00539 buttons=QMessageBox.Ok|QMessageBox.Cancel
00540 defaultButton=QMessageBox.Cancel
00541 if QMessageBox.question(
00542 None,
00543 QApplication.translate("Dialog","Voir les copies",None, QApplication.UnicodeUTF8),
00544 QApplication.translate("Dialog","Voulez-vous voir les fichiers copiés ?",None, QApplication.UnicodeUTF8),
00545 buttons, defaultButton)==QMessageBox.Ok:
00546 subprocess.call("nautilus '%s'" %destDir,shell=True)
00547 return True
00548 else:
00549 msgBox=QMessageBox.warning(
00550 None,
00551 QApplication.translate("Dialog","Destination manquante",None, QApplication.UnicodeUTF8),
00552 QApplication.translate("Dialog","Veuillez choisir une destination pour la copie des fichiers",None, QApplication.UnicodeUTF8))
00553 return True
00554
00555
00556
00557
00558
00559
00560 def redoCmd(self):
00561 global lastCommand, pastCommands, activeThreads
00562 if len(activeThreads)>0:
00563 for thread in self.oldThreads:
00564 if thread.isAlive():
00565 try:
00566 thread._Thread__stop()
00567 print str(thread.getName()) + ' is terminated'
00568 except:
00569 print str(thread.getName()) + ' could not be terminated'
00570 else:
00571 if lastCommand==None:
00572 return
00573 if QMessageBox.question(
00574 None,
00575 QApplication.translate("Dialog","Réitérer la dernière commande",None, QApplication.UnicodeUTF8),
00576 QApplication.translate("Dialog","La dernière commande était<br>%1<br>Voulez-vous la relancer avec les nouveaux baladeurs ?",None, QApplication.UnicodeUTF8).arg(lastCommand))==QMessageBox.Cancel:
00577 return
00578 for p in qApp.diskData:
00579 if p.owner in pastCommands[lastCommand] : continue
00580 exec(compile(lastCommand,'<string>','exec'))
00581 t.setDaemon(True)
00582 t.start()
00583 self.oldThreads.add(t)
00584 pastCommands[lastCommand].append(p.owner)
00585
00586
00587
00588
00589
00590
00591 def namesCmd(self):
00592 self.namesDialog.show()
00593
00594
00595
00596
00597
00598 def help(self):
00599 w=help.helpWindow(self)
00600 w.show()
00601 w.exec_()
00602
00603
00604
00605
00606
00607 def umount(self):
00608 buttons=QMessageBox.Ok|QMessageBox.Cancel
00609 defaultButton=QMessageBox.Cancel
00610 button=QMessageBox.question (
00611 self,
00612 QApplication.translate("Main","Démontage des baladeurs",None, QApplication.UnicodeUTF8),
00613 QApplication.translate("Main","Êtes-vous sûr de vouloir démonter tous les baladeurs cochés de la liste ?",None, QApplication.UnicodeUTF8),
00614 buttons,defaultButton)
00615 if button!=QMessageBox.Ok:
00616 return
00617
00618 for p in qApp.diskData:
00619
00620 for d in qApp.diskData.disks.keys():
00621 if p in qApp.diskData.disks[d] and p.selected:
00622
00623 for partition in qApp.diskData.disks[d]:
00624 devfile=partition.getProp("device-file-by-id")
00625 if isinstance(devfile, dbus.Array):
00626 devfile=devfile[0]
00627 if partition.isMounted():
00628 subprocess.call("udisks --unmount %s" %devfile, shell=True)
00629
00630 devfile_disk=d.getProp("device-file-by-id")
00631 if isinstance(devfile_disk, dbus.Array):
00632 devfile_disk=devfile_disk[0]
00633 subprocess.call("udisks --detach %s" %devfile_disk, shell=True)
00634 break
00635 self.checkDisks()
00636 self.operations=[]
00637
00638
00639
00640
00641
00642
00643
00644 def connectTableModel(self, data):
00645 self.visibleheader=[]
00646 for h in self.header:
00647 if h in ownedUsbDisk.uDisk._itemNames:
00648 self.visibleheader.append(self.tr(ownedUsbDisk.uDisk._itemNames[h]))
00649 else:
00650 self.visibleheader.append(h)
00651 self.tm=usbTableModel(self, self.visibleheader,data,self.checkable)
00652 self.t.setModel(self.tm)
00653 if self.checkable:
00654 self.t.setItemDelegateForColumn(0, CheckBoxDelegate(self))
00655 self.t.setItemDelegateForColumn(1, UsbDiskDelegate(self))
00656 self.t.setItemDelegateForColumn(3, DiskSizeDelegate(self))
00657 else:
00658 self.t.setItemDelegateForColumn(0, UsbDiskDelegate(self))
00659 self.t.setItemDelegateForColumn(2, DiskSizeDelegate(self))
00660 self.proxy.setSourceModel(self.t.model())
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675 def checkDisks(self, force=False, noLoop=True):
00676 if self.checkDisksLock:
00677
00678 return
00679 self.checkDisksLock=True
00680 other=ownedUsbDisk.Available(
00681 self.checkable,
00682 access="firstFat",
00683 diskDict=self.listener.connectedVolumes,
00684 noLoop=noLoop)
00685 if force or not self.sameDiskData(qApp.diskData, other):
00686 qApp.diskData=other
00687 connectedCount=int(other)
00688 self.connectTableModel(other)
00689 self.updateButtons()
00690 self.t.resizeColumnsToContents()
00691 self.ui.lcdNumber.display(connectedCount)
00692 self.flashLCD()
00693
00694 if self.checkable:
00695 col=1
00696 else:
00697 col=0
00698 self.t.horizontalHeader().setSortIndicator(1, Qt.AscendingOrder);
00699 self.t.setSortingEnabled(True)
00700 self.t.resizeColumnsToContents()
00701 self.checkDisksLock=False
00702
00703
00704
00705
00706
00707
00708 def sameDiskData(self, one, two):
00709 return set([p.uniqueId() for p in one]) == set([p.uniqueId() for p in two])
00710
00711
00712
00713
00714
00715 def flashLCD(self):
00716 self.ui.lcdNumber.setBackgroundRole(QPalette.Highlight)
00717 self.flashTimer.start(250)
00718
00719
00720
00721
00722
00723 def normalLCD(self):
00724 self.ui.lcdNumber.setBackgroundRole(QPalette.Window)
00725
00726
00727
00728
00729
00730 class usbTableModel(QAbstractTableModel):
00731
00732
00733
00734
00735
00736
00737
00738
00739 def __init__(self, parent=None, header=[], donnees=None, checkable=False):
00740 QAbstractTableModel.__init__(self,parent)
00741 self.header=header
00742 self.donnees=donnees
00743 self.checkable=checkable
00744 self.pere=parent
00745 self.connect(self, SIGNAL("pushCmd(QString, QString)"), self.pushCmd)
00746 self.connect(self, SIGNAL("popCmd(QString, QString)"), self.popCmd)
00747
00748
00749
00750
00751
00752
00753
00754 def pushCmd(self,owner,cmd):
00755 global activeThreads, pastCommands, lastCommand
00756 owner=u"%s" %owner
00757 owner=owner.encode("utf-8")
00758 if activeThreads.has_key(owner):
00759 activeThreads[owner].append(cmd)
00760 else:
00761 activeThreads[owner]=[cmd]
00762 self.updateOwnerColumn()
00763 self.pere.updateButtons()
00764
00765
00766
00767
00768
00769
00770
00771 def popCmd(self,owner, cmd):
00772 global activeThreads, pastCommands, lastCommand
00773 owner=u"%s" %owner
00774 owner=owner.encode("utf-8")
00775 if activeThreads.has_key(owner):
00776 cmd0=activeThreads[owner].pop()
00777 if cmd0 in cmd:
00778 msg=cmd.replace(cmd0,"")+"\n"
00779 logFile=open(os.path.expanduser(logFileName),"a")
00780 logFile.write(msg)
00781 logFile.close()
00782 else:
00783 raise Exception, (u"mismatched commands\n%s\n%s" %(cmd,cmd0)).encode("utf-8")
00784 if len(activeThreads[owner])==0:
00785 activeThreads.pop(owner)
00786 else:
00787 raise Exception, "End of command without a begin."
00788
00789 self.updateOwnerColumn()
00790 if len(activeThreads)==0 :
00791 self.pere.updateButtons()
00792
00793
00794
00795
00796
00797 def updateOwnerColumn(self):
00798 if self.checkable:
00799 column=1
00800 else:
00801 column=0
00802 self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), self.index(0,column), self.index(len(self.donnees)-1, column))
00803 self.pere.t.viewport().update()
00804
00805
00806
00807
00808
00809 def rowCount(self, parent):
00810 return len(self.donnees)
00811
00812
00813
00814
00815
00816 def columnCount(self, parent):
00817 return len(self.header)
00818
00819 def setData(self, index, value, role):
00820 if index.column()==0 and self.checkable:
00821 self.donnees[index.row()].selected=value
00822 return True
00823 else:
00824 return QAbstractTableModel.setData(self, index, role)
00825
00826
00827
00828
00829
00830
00831 def partition(self, index):
00832 return self.donnees[index.row()][-1]
00833
00834 def data(self, index, role):
00835 if not index.isValid():
00836 return QVariant()
00837 elif role==Qt.ToolTipRole:
00838 c=index.column()
00839 h=self.pere.header[c]
00840 if c==0 and self.checkable:
00841 return QApplication.translate("Main","Cocher ou décocher cette case en cliquant.<br><b>Double-clic</b> pour agir sur plusieurs baladeurs.",None, QApplication.UnicodeUTF8)
00842 elif c==1:
00843 return QApplication.translate("Main","Propriétaire de la clé USB ou du baladeur ;<br><b>Double-clic</b> pour modifier.",None, QApplication.UnicodeUTF8)
00844 elif "device-mount-paths" in h:
00845 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)
00846 elif "device-size" in h:
00847 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)
00848 elif "drive-vendor" in h:
00849 return QApplication.translate("Main","Fabricant de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00850 elif "drive-model" in h:
00851 return QApplication.translate("Main","Modèle de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00852 elif "drive-serial" in h:
00853 return QApplication.translate("Main","Numéro de série de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00854 else:
00855 return ""
00856 elif role != Qt.DisplayRole:
00857 return QVariant()
00858 if index.row()<len(self.donnees):
00859 return QVariant(self.donnees[index.row()][index.column()])
00860 else:
00861 return QVariant()
00862
00863 def headerData(self, section, orientation, role):
00864 if orientation == Qt.Horizontal and role == Qt.DisplayRole:
00865 return QVariant(self.header[section])
00866 elif orientation == Qt.Vertical and role == Qt.DisplayRole:
00867 return QVariant(section+1)
00868 return QVariant()
00869
00870
00871
00872
00873
00874
00875 def sort(self, Ncol, order=Qt.DescendingOrder):
00876 self.emit(SIGNAL("layoutAboutToBeChanged()"))
00877 self.donnees = sorted(self.donnees, key=operator.itemgetter(Ncol))
00878 if order == Qt.DescendingOrder:
00879 self.donnees.reverse()
00880 self.emit(SIGNAL("layoutChanged()"))
00881
00882
00883
00884
00885
00886
00887
00888 def CheckBoxRect(view_item_style_options):
00889 check_box_style_option=QStyleOptionButton()
00890 check_box_rect = QApplication.style().subElementRect(QStyle.SE_CheckBoxIndicator,check_box_style_option)
00891 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)
00892 return QRect(check_box_point, check_box_rect.size())
00893
00894 class CheckBoxDelegate(QStyledItemDelegate):
00895 def __init__(self, parent):
00896 QStyledItemDelegate.__init__(self,parent)
00897
00898 def paint(self, painter, option, index):
00899 checked = index.model().data(index, Qt.DisplayRole).toBool()
00900 check_box_style_option=QStyleOptionButton()
00901 check_box_style_option.state |= QStyle.State_Enabled
00902 if checked:
00903 check_box_style_option.state |= QStyle.State_On
00904 else:
00905 check_box_style_option.state |= QStyle.State_Off
00906 check_box_style_option.rect = CheckBoxRect(option);
00907 QApplication.style().drawControl(QStyle.CE_CheckBox, check_box_style_option, painter)
00908
00909 def editorEvent(self, event, model, option, index):
00910 if ((event.type() == QEvent.MouseButtonRelease) or (event.type() == QEvent.MouseButtonDblClick)):
00911 if (event.button() != Qt.LeftButton or not CheckBoxRect(option).contains(event.pos())):
00912 return False
00913 if (event.type() == QEvent.MouseButtonDblClick):
00914 return True
00915 elif (event.type() == QEvent.KeyPress):
00916 if event.key() != Qt.Key_Space and event.key() != Qt.Key_Select:
00917 return False
00918 else:
00919 return False
00920 checked = index.model().data(index, Qt.DisplayRole).toBool()
00921 result = model.setData(index, not checked, Qt.EditRole)
00922 return result
00923
00924
00925
00926
00927
00928
00929
00930
00931 class UsbDiskDelegate(QStyledItemDelegate):
00932 def __init__(self, parent):
00933 QStyledItemDelegate.__init__(self,parent)
00934 self.okPixmap=QPixmap("/usr/share/icons/Tango/16x16/status/weather-clear.png")
00935 self.busyPixmap=QPixmap("/usr/share/icons/Tango/16x16/actions/view-refresh.png")
00936
00937 def paint(self, painter, option, index):
00938 global activeThreads
00939 text = index.model().data(index, Qt.DisplayRole).toString()
00940 rect0=QRect(option.rect)
00941 rect1=QRect(option.rect)
00942 h=rect0.height()
00943 w=rect0.width()
00944 rect0.setSize(QSize(h,h))
00945 rect1.translate(h,0)
00946 rect1.setSize(QSize(w-h,h))
00947 QApplication.style().drawItemText (painter, rect1, Qt.AlignLeft+Qt.AlignVCenter, option.palette, True, text)
00948 QApplication.style().drawItemText (painter, rect0, Qt.AlignCenter, option.palette, True, QString("O"))
00949 text=(u"%s" %text).encode("utf-8")
00950 if activeThreads.has_key(text):
00951 QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.busyPixmap)
00952 else:
00953 QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.okPixmap)
00954
00955
00956
00957
00958
00959
00960
00961 class DiskSizeDelegate(QStyledItemDelegate):
00962 def __init__(self, parent):
00963 QStyledItemDelegate.__init__(self,parent)
00964
00965
00966 def paint(self, painter, option, index):
00967 value = int(index.model().data(index, Qt.DisplayRole).toString())
00968 text = self.val2txt(value)
00969 rect0=QRect(option.rect)
00970 rect1=QRect(option.rect)
00971 rect0.translate(2,(rect0.height()-16)/2)
00972 rect0.setSize(QSize(16,16))
00973 rect1.translate(20,0)
00974 rect1.setWidth(rect1.width()-20)
00975 QApplication.style().drawItemText (painter, rect1, Qt.AlignLeft+Qt.AlignVCenter, option.palette, True, text)
00976
00977 mount=index.model().partition(index).mountPoint()
00978 dev,total,used,remain,pcent,path = self.parent().diskSizeData(mount)
00979 pcent=int(pcent[:-1])
00980 painter.setBrush(QBrush(QColor("slateblue")))
00981 painter.drawPie(rect0,0,16*360*pcent/100)
00982
00983
00984
00985
00986
00987 def val2txt(self, val):
00988 suffixes=["B", "KB", "MB", "GB", "TB"]
00989 val*=1.0
00990 i=0
00991 while val > 1024 and i < len(suffixes):
00992 i+=1
00993 val/=1024
00994 return "%4.1f %s" %(val, suffixes[i])
00995
00996