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, preferences
00029 import os.path, operator, subprocess, dbus, re, time
00030 from notification import Notification
00031 import db
00032 from globaldef import logFileName
00033
00034
00035 globalDiskData=ownedUsbDisk.Available(True,access="firstFat")
00036
00037
00038
00039
00040
00041
00042 def firstdir(l):
00043 for d in l:
00044 if os.path.isdir(d): return d
00045 return None
00046
00047
00048
00049
00050
00051
00052 def _dir(which):
00053 if which=="lang":
00054 return firstdir(["/usr/share/scolasync/lang", "lang"])
00055 elif which=="help":
00056 return firstdir(["/usr/share/scolasync/help", "help"])
00057 elif which=="share":
00058 return firstdir(["/usr/share/scolasync/","share"])
00059 return None
00060
00061
00062 class mainWindow(QMainWindow):
00063
00064
00065
00066
00067
00068
00069
00070 def __init__(self, parent, opts, locale="fr_FR"):
00071 QMainWindow.__init__(self)
00072 QWidget.__init__(self, parent)
00073 self.locale=locale
00074 from Ui_mainWindow import Ui_MainWindow
00075 self.ui = Ui_MainWindow()
00076 self.ui.setupUi(self)
00077 self.t=self.ui.tableView
00078 self.opts=opts
00079 self.applyPreferences()
00080 self.timer=QTimer()
00081 self.updateButtons()
00082 self.timer.start(2000)
00083 self.threads=[]
00084 QObject.connect(self.timer, SIGNAL("timeout()"), self.checkDisks)
00085 QObject.connect(self.ui.helpButton, SIGNAL("clicked()"), self.help)
00086 QObject.connect(self.ui.umountButton, SIGNAL("clicked()"), self.umount)
00087 QObject.connect(self.ui.toButton, SIGNAL("clicked()"), self.copyTo)
00088 QObject.connect(self.ui.fromButton, SIGNAL("clicked()"), self.copyFrom)
00089 QObject.connect(self.ui.delButton, SIGNAL("clicked()"), self.delFiles)
00090 QObject.connect(self.ui.preferenceButton, SIGNAL("clicked()"), self.preference)
00091 QObject.connect(self.ui.tableView, SIGNAL("doubleClicked(const QModelIndex&)"), self.tableClicked)
00092
00093
00094
00095
00096
00097 def applyPreferences(self):
00098 global globalDiskData
00099 prefs=db.readPrefs()
00100 self.workdir=prefs["workdir"]
00101
00102
00103 self.checkable=("--check","") in self.opts or ("-c","") in self.opts or prefs["checkable"]
00104 other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00105 globalDiskData=other
00106 self.header=ownedUsbDisk.uDisk.headers(self.checkable)
00107 self.connectTableModel(other)
00108 self.t.setSortingEnabled(True)
00109 self.t.resizeColumnsToContents()
00110
00111
00112
00113
00114
00115
00116 def changeWd(self, newDir):
00117 self.workdir=newDir
00118 db.setWd(newDir)
00119
00120
00121
00122
00123
00124
00125 def tableClicked(self, idx):
00126 global globalDiskData
00127 c=idx.column()
00128 r=idx.row()
00129 h=self.header[c]
00130 if c==0 and self.checkable:
00131
00132 pass
00133 elif c==1:
00134
00135 self.editOwner(r)
00136 elif "device-mount-paths" in h:
00137 cmd=u"nautilus '%s'" %idx.data().toString ()
00138 subprocess.call(cmd, shell=True)
00139 elif "device-size" in h:
00140 path=globalDiskData[r][self.header.index("1device-mount-paths")]
00141 cmd =u"df '%s'" %path
00142 dfOutput=subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0].split("\n")[-2]
00143 m = re.match("(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+).*", dfOutput).groups()
00144 print m
00145 dev,total,used,remain,pcent,path=m
00146 pcent=int(pcent[:-1])
00147 from diskFull import mainWindow
00148 w=mainWindow(self,pcent,title=path, total=total, used=used)
00149 w.show()
00150 else:
00151 QMessageBox.warning(None,
00152 QApplication.translate("Dialog","Double-clic non pris en compte",None, QApplication.UnicodeUTF8),
00153 QApplication.translate("Dialog","pas d'action pour l'attribut %1",None, QApplication.UnicodeUTF8).arg(h))
00154
00155
00156
00157
00158
00159
00160
00161 def diskFromTableRow(self,ligne):
00162 global globalDiskData
00163 stickid=globalDiskData[ligne][self.header.index("5drive-serial")]
00164 found=False
00165 for d in globalDiskData.disks.keys():
00166 if d.showableProp("drive-serial")==stickid:
00167 found=True
00168 break
00169 if found:
00170 return d
00171 else:
00172 return None
00173
00174
00175
00176
00177
00178
00179 def editOwner(self, ligne):
00180 global globalDiskData
00181 student=globalDiskData[ligne][1]
00182 ownedUsbDisk.editRecord(self.diskFromTableRow(ligne), student)
00183 other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00184 globalDiskData=other
00185 self.connectTableModel(other)
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195 def updateButtons(self):
00196 global globalDiskData
00197 if len(globalDiskData)>0:
00198 self.ui.toButton.setEnabled(True)
00199 self.ui.fromButton.setEnabled(True)
00200 self.ui.umountButton.setEnabled(True)
00201 else:
00202 self.ui.toButton.setEnabled(False)
00203 self.ui.fromButton.setEnabled(False)
00204 self.ui.umountButton.setEnabled(False)
00205
00206
00207
00208
00209
00210 def preference(self):
00211 pref=preferences.preferenceWindow()
00212 pref.setValues(db.readPrefs())
00213 pref.show()
00214 pref.exec_()
00215 if pref.result()==QDialog.Accepted:
00216 db.writePrefs(pref.values())
00217
00218 self.applyPreferences()
00219
00220
00221
00222
00223
00224 def delFiles(self):
00225 global globalDiskData
00226 titre1=QApplication.translate("Dialog","Choix de fichiers à supprimer",None, QApplication.UnicodeUTF8)
00227 titre2=QApplication.translate("Dialog","Choix de fichiers à supprimer (jokers autorisés)",None, QApplication.UnicodeUTF8)
00228 d=chooseInSticks.chooseDialog(self, titre1, titre2)
00229 ok = d.exec_()
00230 if ok:
00231 pathList=map(lambda x: u"%s" %x, d.pathList())
00232 reply=QMessageBox.critical(None,
00233 QApplication.translate("Dialog","Vous allez effacer plusieurs baladeurs",None, QApplication.UnicodeUTF8),
00234 QApplication.translate("Dialog","Etes-vous certain de vouloir effacer : "+",".join(pathList),None, QApplication.UnicodeUTF8))
00235 if reply == QMessageBox.Ok:
00236 for p in globalDiskData:
00237 t=usbThread.threadDeleteInUSB(p,pathList,subdir="Travail", logfile=logFileName)
00238 t.setDaemon(True)
00239 self.threads.append(t)
00240 t.start()
00241 else:
00242 msgBox=QMessageBox.warning(None,
00243 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00244 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00245
00246
00247
00248
00249
00250 def copyTo(self):
00251 global globalDiskData
00252 d=copyToDialog1.copyToDialog1(parent=self, workdir=self.workdir)
00253 d.exec_()
00254 if d.ok==True:
00255 for p in globalDiskData:
00256 subdir=self.workdir
00257 t=usbThread.threadCopyToUSB(p,d.selectedList(),subdir=subdir, logfile=logFileName)
00258 t.setDaemon(True)
00259 self.threads.append(t)
00260 t.start()
00261 else:
00262 msgBox=QMessageBox.warning(None,
00263 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00264 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00265
00266
00267
00268
00269
00270 def copyFrom(self):
00271 global globalDiskData
00272 titre1=QApplication.translate("Dialog","Choix de fichiers à copier",None, QApplication.UnicodeUTF8)
00273 titre2=QApplication.translate("Dialog", "Choix de fichiers à copier depuis les baladeurs", None, QApplication.UnicodeUTF8)
00274 ok=QApplication.translate("Dialog", "Choix de la destination ...", None, QApplication.UnicodeUTF8)
00275 d=chooseInSticks.chooseDialog(self, title1=titre1, title2=titre2, ok=ok)
00276 ok = d.exec_()
00277 if not ok or len(d.pathList())==0 :
00278 msgBox=QMessageBox.warning(None,
00279 QApplication.translate("Dialog","Aucun fichier sélectionné",None, QApplication.UnicodeUTF8),
00280 QApplication.translate("Dialog","Veuillez choisir au moins un fichier",None, QApplication.UnicodeUTF8))
00281 return
00282
00283 pathList=map(lambda x: u"%s" %x, d.pathList())
00284 mp=d.selectedDiskMountPoint()
00285 initialPath=os.path.expanduser("~")
00286 destDir = QFileDialog.getExistingDirectory(None,
00287 QApplication.translate("Dialog","Choisir un répertoire de destination",None, QApplication.UnicodeUTF8),
00288 initialPath)
00289 if destDir and len(destDir)>0 :
00290 dest=u"%s" %destDir
00291 for p in globalDiskData:
00292
00293
00294
00295
00296
00297 t=usbThread.threadCopyFromUSB(p,pathList,subdir=self.workdir,
00298 rootPath=mp,
00299 dest=dest,
00300 logfile=logFileName)
00301 t.setDaemon(True)
00302 self.threads.append(t)
00303 t.start()
00304
00305 if QMessageBox.question(None,
00306 QApplication.translate("Dialog","Voir les copies",None, QApplication.UnicodeUTF8),
00307 QApplication.translate("Dialog","Voulez-vous voir les fichiers copiés ?",None, QApplication.UnicodeUTF8)):
00308 subprocess.call("nautilus '%s'" %dest,shell=True)
00309 else:
00310 msgBox=QMessageBox.warning(None,
00311 QApplication.translate("Dialog","Destination manquante",None, QApplication.UnicodeUTF8),
00312 QApplication.translate("Dialog","Veuillez choisir une destination pour la copie des fichiers",None, QApplication.UnicodeUTF8))
00313
00314
00315
00316
00317
00318
00319
00320 def help(self):
00321 w=help.helpWindow(None)
00322 w.loadBrowsers(_dir("help"),self.locale)
00323 w.show()
00324 w.exec_()
00325
00326
00327
00328
00329
00330 def umount(self):
00331 global globalDiskData
00332 buttons=QMessageBox.StandardButtons(QMessageBox.Ok+QMessageBox.Cancel)
00333 button=QMessageBox.critical ( self,
00334 QApplication.translate("Main","Démontage des baladeurs",None, QApplication.UnicodeUTF8),
00335 QApplication.translate("Main","Êtes-vous sûr de vouloir démonter tous les baladeurs cochés de la liste ?",None, QApplication.UnicodeUTF8),
00336 buttons=buttons)
00337 if button!=QMessageBox.Ok:
00338 return
00339
00340 for p in globalDiskData:
00341
00342 for d in globalDiskData.disks.keys():
00343 if p in globalDiskData.disks[d] and p.selected:
00344
00345 for partition in globalDiskData.disks[d]:
00346 devfile=partition.getProp("device-file-by-id")
00347 if isinstance(devfile, dbus.Array):
00348 devfile=devfile[0]
00349 subprocess.call("udisks --unmount %s" %devfile, shell=True)
00350
00351 devfile_disk=d.getProp("device-file-by-id")
00352 if isinstance(devfile_disk, dbus.Array):
00353 devfile_disk=devfile_disk[0]
00354 subprocess.call("udisks --detach %s" %devfile_disk, shell=True)
00355 break
00356
00357
00358
00359
00360
00361
00362
00363 def connectTableModel(self, data):
00364 self.visibleheader=[]
00365 for h in self.header:
00366 if h in ownedUsbDisk.uDisk._itemNames:
00367 self.visibleheader.append(self.tr(ownedUsbDisk.uDisk._itemNames[h]))
00368 else:
00369 self.visibleheader.append(h)
00370 self.tm=usbTableModel(self, self.visibleheader,data,self.checkable)
00371 self.t.setModel(self.tm)
00372 if self.checkable:
00373 self.t.setItemDelegateForColumn(0, CheckBoxDelegate(self))
00374 self.t.setItemDelegateForColumn(1, UsbDiskDelegate(self))
00375 else:
00376 self.t.setItemDelegateForColumn(0, UsbDiskDelegate(self))
00377
00378
00379
00380
00381
00382
00383
00384
00385 def checkDisks(self):
00386 global globalDiskData
00387 other=ownedUsbDisk.Available(self.checkable,access="firstFat")
00388 globalDiskData=other
00389 self.connectTableModel(other)
00390 self.updateButtons()
00391 self.t.resizeColumnsToContents()
00392
00393
00394
00395
00396
00397
00398
00399 class usbTableModel(QAbstractTableModel):
00400
00401
00402
00403
00404
00405
00406
00407
00408 def __init__(self, parent=None, header=[], donnees=None, checkable=False):
00409 QAbstractTableModel.__init__(self,parent)
00410 self.header=header
00411 self.donnees=donnees
00412 self.checkable=checkable
00413 self.pere=parent
00414
00415
00416
00417
00418
00419 def rowCount(self, parent):
00420 return len(self.donnees)
00421
00422
00423
00424
00425
00426 def columnCount(self, parent):
00427 return len(self.header)
00428
00429 def setData(self, index, value, role):
00430 if index.column()==0 and self.checkable:
00431 self.donnees[index.row()].selected=value
00432 return True
00433 else:
00434 return QAbstractTableModel.setData(self, index, role)
00435
00436 def data(self, index, role):
00437 if not index.isValid():
00438 return QVariant()
00439 elif role==Qt.ToolTipRole:
00440 c=index.column()
00441 h=self.pere.header[c]
00442 if c==0 and self.checkable:
00443 return QApplication.translate("Main","Cocher ou décocher cette case en cliquant.",None, QApplication.UnicodeUTF8)
00444 elif c==1:
00445 return QApplication.translate("Main","Propriétaire de la clé USB ou du baladeur ;<br><b>Double-clic</b> pour modifier.",None, QApplication.UnicodeUTF8)
00446 elif "device-mount-paths" in h:
00447 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)
00448 elif "device-size" in h:
00449 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)
00450 elif "drive-vendor" in h:
00451 return QApplication.translate("Main","Fabricant de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00452 elif "drive-model" in h:
00453 return QApplication.translate("Main","Modèle de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00454 elif "drive-serial" in h:
00455 return QApplication.translate("Main","Numéro de série de la clé USB ou du baladeur.",None, QApplication.UnicodeUTF8)
00456 else:
00457 return ""
00458 elif role != Qt.DisplayRole:
00459 return QVariant()
00460 if index.row()<len(self.donnees):
00461 return QVariant(self.donnees[index.row()][index.column()])
00462 else:
00463 return QVariant()
00464
00465 def headerData(self, section, orientation, role):
00466 if orientation == Qt.Horizontal and role == Qt.DisplayRole:
00467 return QVariant(self.header[section])
00468 elif orientation == Qt.Vertical and role == Qt.DisplayRole:
00469 return QVariant(section+1)
00470 return QVariant()
00471
00472
00473
00474
00475
00476
00477 def sort(self, Ncol, order=Qt.DescendingOrder):
00478 self.emit(SIGNAL("layoutAboutToBeChanged()"))
00479 self.donnees = sorted(self.donnees, key=operator.itemgetter(Ncol))
00480 if order == Qt.DescendingOrder:
00481 self.donnees.reverse()
00482 self.emit(SIGNAL("layoutChanged()"))
00483
00484 def CheckBoxRect(view_item_style_options):
00485 check_box_style_option=QStyleOptionButton()
00486 check_box_rect = QApplication.style().subElementRect(QStyle.SE_CheckBoxIndicator,check_box_style_option)
00487 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)
00488 return QRect(check_box_point, check_box_rect.size())
00489
00490 class CheckBoxDelegate(QStyledItemDelegate):
00491 def __init__(self, parent):
00492 QStyledItemDelegate.__init__(self,parent)
00493
00494 def paint(self, painter, option, index):
00495 checked = index.model().data(index, Qt.DisplayRole).toBool()
00496 check_box_style_option=QStyleOptionButton()
00497 check_box_style_option.state |= QStyle.State_Enabled
00498 if checked:
00499 check_box_style_option.state |= QStyle.State_On
00500 else:
00501 check_box_style_option.state |= QStyle.State_Off
00502 check_box_style_option.rect = CheckBoxRect(option);
00503 QApplication.style().drawControl(QStyle.CE_CheckBox, check_box_style_option, painter)
00504
00505 def editorEvent(self, event, model, option, index):
00506 if ((event.type() == QEvent.MouseButtonRelease) or (event.type() == QEvent.MouseButtonDblClick)):
00507 if (event.button() != Qt.LeftButton or not CheckBoxRect(option).contains(event.pos())):
00508 return False
00509 if (event.type() == QEvent.MouseButtonDblClick):
00510 return True
00511 elif (event.type() == QEvent.KeyPress):
00512 if event.key() != Qt.Key_Space and event.key() != Qt.Key_Select:
00513 return False
00514 else:
00515 return False
00516 checked = index.model().data(index, Qt.DisplayRole).toBool()
00517 result = model.setData(index, not checked, Qt.EditRole)
00518 return result
00519
00520
00521 class UsbDiskDelegate(QStyledItemDelegate):
00522 def __init__(self, parent):
00523 QStyledItemDelegate.__init__(self,parent)
00524 self.okPixmap=QPixmap("/usr/share/icons/Tango/16x16/status/weather-clear.png")
00525 self.busyPixmap=QPixmap("/usr/share/icons/Tango/16x16/actions/view-refresh.png")
00526
00527 def paint(self, painter, option, index):
00528 global globalDiskData
00529 text = index.model().data(index, Qt.DisplayRole).toString()
00530 rect0=QRect(option.rect)
00531 rect1=QRect(option.rect)
00532 h=rect0.height()
00533 w=rect0.width()
00534 rect0.setSize(QSize(h,h))
00535 rect1.translate(h,0)
00536 rect1.setSize(QSize(w-h,h))
00537 QApplication.style().drawItemText (painter, rect1, Qt.AlignLeft+Qt.AlignVCenter, option.palette, True, text)
00538 QApplication.style().drawItemText (painter, rect0, Qt.AlignCenter, option.palette, True, QString("O"))
00539 if usbThread.globalThreads.busy(u"%s" %text):
00540 QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.busyPixmap)
00541 else:
00542 QApplication.style().drawItemPixmap (painter, rect0, Qt.AlignCenter, self.okPixmap)
00543
00544