ScolaSync: Fichier source de src/usbDisk2.py

ScolaSync 5.1 |

usbDisk2.py
Aller à la documentation de ce fichier.
1 # $Id: usbDisk2.py 36 2014-03-16 19:37:27Z georgesk $
2
3 licence={}
4 licence_en="""
5 file usbDisk2.py
6 this file is part of the project scolasync. It is a rewrite of
7 usbDisk.py to take in account udisks2.
8
9 Copyright (C) 2014 Georges Khaznadar <georgesk@ofset.org>
10
11 This program is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version3 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 """
24
25 licence['en']=licence_en
26 dependences="python3-dbus python3-dbus.mainloop.qt"
27
28
29 import dbus, subprocess, os, os.path, re, time, threading, logging, inspect
30 from dbus.mainloop.glib import DBusGMainLoop, threads_init
31 from gi.repository import Gio, GLib, UDisks
32 from PyQt5.QtWidgets import *
33
34 #################### activate debugging #######################
35 debug=False
36 def inspectData():
37 return ""
38
39 if debug :
40 logging.basicConfig(level=logging.DEBUG)
41 def inspectData():
42 caller=1
43 callerframerecord = inspect.stack()[caller]
44 frame = callerframerecord[0]
45 info = inspect.getframeinfo(frame)
46 return " -- file={0}, function={1}, line={2}".format(
47 info.filename, info.function, info.lineno
48 )
49 else:
50 pass
51 # logging.basicConfig(level=logging.NOTSET)
52 ###############################################################
53
54
55 ##
56 #
57 # Récupère de façon sûre le path d'une instance de UDisksObjectProxy
58 # @param obj instance de UDisksObjectProxy, ou simple chaine
59 #
60 def safePath(obj):
61 if type(obj)==type(""):
62 path=obj
63 else:
64 path= obj.get_object_path()
65 posUnderscore=path.rfind("_")
66 posSlash=path.rfind("/")
67 if posUnderscore > posSlash: # il faut retirer tout après l'underscore final
68 path=path[:posUnderscore]
69 return path
70
71 ##
72 #
73 # Renvoie la taille d'un système de fichier et la place disponible
74 # @return un tuple : taille totale et espace libre
75 #
76 def fs_size(device):
77 try:
78 stat = os.statvfs(device)
79 except:
80 return (0, 0)
81 free = stat.f_bsize * stat.f_bavail # les blocs réservés sont inclus
82 total = stat.f_bsize * stat.f_blocks
83 return (total, free)
84
85
86 ############ la variable suivante a été recopiées à l'aveugle ################
87 ############ depuis un fichier du projet USBcreator ##########################
88 no_options = GLib.Variant('a{sv}', {})
89 ##############################################################################
90 ##############################################################################
91
92 ######### des "chemins" correspondant à des disques non débranchables ########
93 not_interesting = (
94 # boucle
95 '/org/freedesktop/UDisks2/block_devices/loop',
96 # disque raid
97 '/org/freedesktop/UDisks2/block_devices/dm_',
98 # mémoire vive
99 '/org/freedesktop/UDisks2/block_devices/ram',
100 '/org/freedesktop/UDisks2/block_devices/zram',
101 # disques durs
102 '/org/freedesktop/UDisks2/drives/',
103 )
104
105 ##
106 #
107 # Cette classe a été inspirée par le projet USBcreator.
108 # Plusieurs modifications ont été faites au code original.
109 # Les fonctions de rappel ne tiennent compte que des périphériques USB
110 #
111 class UDisksBackend:
112 ##
113 #
114 # Le constructeur.
115 # @param logger un objet permettant de journaliser les messages ;
116 # par défaut il se confond avec le module logging
117 # @param diskClass la classe à utiliser pour créer des instances de disques
118 #
119 def __init__(self, logger=logging, diskClass=object):
120 self.install_thread = None
121 self.logger=logger
122 ## self.targets est un dictionnaire des disques détectés
123 ## les clés sont les paths et les contenus des instances de diskClass
124 self.diskClass=diskClass
125 self.targets = {}
126 ## self.modified signifie une modification récente, à prendre en compte
127 ## par une application au niveau utilisateur
128 self.modified=False
129 DBusGMainLoop(set_as_default=True)
130 threads_init()
131 self.bus = dbus.SystemBus()
132 self.udisks = UDisks.Client.new_sync(None)
133 self.manager = self.udisks.get_object_manager()
134 self.cbHooks = {
135 'object-added': {
136 'profile': ['man', 'obj'],
137 'hooks' : []
138 },
139 'object-removed': {
140 'profile': ['man', 'obj'],
141 'hooks' : []
142 },
143 'interface-added': {
144 'profile': ['man', 'obj'],
145 'hooks' : []
146 },
147 'interface-removed': {
148 'profile': ['man', 'obj'],
149 'hooks' : []
150 },
151 'interface-proxy-properties-changed': {
152 'profile': ['man', 'obj', 'interface'],
153 'hooks' : []
154 },
155 }
156 # mise en place des fonctions de rappel à utiliser pour tout changement
157 self.addHook('object-added',
158 lambda man, obj: self._udisks_obj_added(obj))
159 self.addHook('object-removed',
160 lambda man, obj: self._udisks_obj_removed(obj))
161 self.addHook('interface-added',
162 lambda man, obj, iface: self._device_changed(obj))
163 self.addHook('interface-removed',
164 lambda man, obj, iface: self._device_changed(obj))
165 self.addHook('interface-proxy-properties-changed',
166 lambda man, obj, iface, props, invalid: self._device_changed(obj))
167
168 ##
169 #
170 # ajoute une fonction à appeler pour un signal nommé, et enregistre
171 # cette fonction dans self.cbHooks, après vérification de sa liste
172 # de paramètres.
173 # @param signal une chaîne
174 # @param func une fonction
175 # @return le résultat de l'appel à self.manager.connect(signal,func)
176 #
177 def addHook(self, signal, func):
178 if inspect.getargspec(func).args == self.cbHooks[signal]['profile']:
179 cb=self.manager.connect(signal,func)
180 self.cbHooks[signal]['hooks'].append(cb)
181 return cb
182 return None
183
184 # voir le fichier integration-test issu des sources de udisks2
185 ##
186 #
187 # Essaie de monter un système de fichier jusqu'à ce qu'il
188 # cesse d'échouer avec "Busy", ou que l'erreur soit "déjà monté".
189 # Échoue si l'erreur est autre que les deux précédentes.
190 # @param fs un système de fichier à monter
191 # @param timeout nombre de secondes d'attente au maximum
192 # @param retryDelay délai entre deux essais
193 #
194 def retry_mount(self, fs, timeout=5, retryDelay=0.3):
195 while timeout >= 0:
196 try:
197 return fs.call_mount_sync(no_options, None)
198 except GLib.GError as e:
199 if 'UDisks2.Error.AlreadyMounted' in e.message:
200 m=re.match(r".*already mounted[^/]*([^\']+).*",e.message)
201 return m.group(1)
202 elif 'UDisks2.Error.DeviceBusy' in e.message:
203 pass
204 else:
205 raise
206 time.sleep(retryDelay)
207 timeout -= retryDelay
208 return ''
209
210 ##
211 #
212 # Fait un inventaire des disques.
213 #
214 def detect_devices(self):
215 for obj in self.manager.get_objects():
216 self._udisks_obj_added(obj)
217
218 ##
219 #
220 # trouve si un objet est intéressant à cataloguer
221 # @param obj une instance de UDisksObjectProxy
222 # @return un triplet interesting (vrai/faux), drive (objet),
223 # partition (objet).
224 #
225 def _interesting_obj(self, obj):
226 interesting=False
227 drive=None
228 partition=None
229
230 # ne tient pas compte des périphériques non débranchables
231 path = safePath(obj)
232 for boring in not_interesting:
233 if path.startswith(boring):
234 return interesting, drive, partition
235
236 # ne considère que les périphériques de type block
237 block = obj.get_block()
238 if not block:
239 return interesting, drive, partition
240
241 # initialise drive, nom du disque ?
242 drive_name = block.get_cached_property('Drive').get_string()
243 if drive_name == '/':
244 return interesting, drive, partition
245 else:
246 drive = self.udisks.get_object(drive_name).get_drive()
247
248 # on ne tient pas compte des CDROMS ni DVDROMS
249 if drive and drive.get_cached_property('Optical').get_boolean():
250 return interesting, drive, partition
251
252 interesting=True
253 # détermine si on a un disque ou une partition
254 partition = obj.get_partition()
255 return interesting, drive, partition
256
257 ##
258 #
259 # Fonction de rappel pour les ajouts de disque
260 # @param obj un objet renvoyé par l'évènement
261 #
262 def _udisks_obj_added(self, obj):
263 interesting, drive, part = self._interesting_obj(obj)
264 if part:
265 self._udisks_partition_added(obj, drive, part)
266 elif drive:
267 self._udisks_drive_added (obj, drive, part)
268 return
269
270 ##
271 #
272 # détermine si un périphérique est de type USB
273 # @param obj un objet UDisksObjectProxy
274 # @return vrai si c'est un périphérique USB
275 #
276 def objIsUsb(self,obj):
277 for s in obj.get_block().get_cached_property('Symlinks'):
278 if b'/dev/disk/by-id/usb' in bytes(s):
279 return True
280 return False
281
282 ##
283 #
284 # Fonction de rappel pour l'ajout d'une partition,
285 # met à jour self.targets
286 # @param obj une instance de UDisksObjectProxy
287 # @param drive une instance de ...
288 # @param partition une instance de ...
289 #
290 def _udisks_partition_added(self, obj, drive, partition):
291 path = safePath(obj)
292 block = obj.get_block()
293 self.logger.debug(QApplication.translate("uDisk","Partition ajoutée %s",None) % path+inspectData())
294 fstype = block.get_cached_property('IdType').get_string()
295 parent = partition.get_cached_property('Table').get_string()
296 total = drive.get_cached_property('Size').get_uint64()
297 free = -1
298 mount = ''
299 fs = obj.get_filesystem()
300 if fs:
301 mount_points = fs.get_cached_property('MountPoints').get_bytestring_array()
302 if len(mount_points)>0:
303 mount= mount_points[0]
304 if not mount and fstype == 'vfat':
305 try:
306 mount = self.retry_mount(fs)
307 except:
308 logging.exception(QApplication.translate("uDisk","Échec au montage du disque : %s",None) % path)
309 if mount:
310 total, free = fs_size(mount)
311 isUsb=self.objIsUsb(obj)
312 if not isUsb:
313 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition non-USB",None)+inspectData())
314 elif total < 1:
315 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition vide",None)+inspectData())
316 else:
317 udisk=self.diskClass(
318 path=path, mp=mount, isUsb=isUsb,
319 vendor=drive.get_cached_property('Vendor').get_string(),
320 model=drive.get_cached_property('Model').get_string(),
321 parent=safePath(parent),
322 fstype=fstype,
323 serial=block.get_cached_property('Drive').get_string().split('_')[-1],
324 uuid=block.get_cached_property('IdUUID').get_string(),
325 free=free,
326 capacity=total,
327 device=block.get_cached_property('Device').get_bytestring().decode('utf-8'),
328 )
329 self.targets[path] = udisk
330 self.modified=True
331 return
332
333 def _udisks_drive_added(self, obj, drive, part):
334 path = safePath(obj)
335 block = obj.get_block()
336 if path in self.targets:
337 self.logger.debug(QApplication.translate("uDisk","Disque déjà ajouté auparavant : %s",None) % path+inspectData())
338 return
339 self.logger.debug(QApplication.translate("uDisk","Disque ajouté : %s",None) % path+inspectData())
340 size = drive.get_cached_property('Size').get_uint64()
341 ##### désactivé, quelquefois drive.get_cached_property('Size').get_uint64()
342 ##### renvoie des résultats erronés juste après le branchement
343 """
344 if size <= 0:
345 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition à 0 octets.",None)+inspectData())
346 return
347 """
348 isUsb = self.objIsUsb(obj)
349 if not isUsb:
350 self.logger.debug(QApplication.translate("uDisk","On n'ajoute pas le disque : partition non-USB",None)+inspectData())
351 else:
352 udisk=self.diskClass(
353 path=path,
354 isUsb=isUsb,
355 parent='',
356 vendor=drive.get_cached_property('Vendor').get_string(),
357 model=drive.get_cached_property('Model').get_string(),
358 serial=block.get_cached_property('Drive').get_string().split('_')[-1],
359 uuid=block.get_cached_property('IdUUID').get_string(),
360 capacity=size,
361 device=block.get_cached_property('Device').get_bytestring().decode('utf-8'),
362 )
363 self.targets[path] =udisk
364 self.modified=True
365 return
366
367 def _device_changed(self, obj):
368 path = safePath(obj)
369 self.logger.debug(QApplication.translate("uDisk","Changement pour le disque %s",None) % path+inspectData())
370
371 ##
372 #
373 # Fonction de rappel déclenchée par le retrait d'un disque.
374 # Met à jour self.targets
375 # @param obj une instance de UDisksObjectProxy
376 #
377 def _udisks_obj_removed(self, obj):
378 path=safePath(obj)
379 logging.debug(QApplication.translate("uDisk","Disque débranché du système : %s",None) % path)
380 if path in self.targets:
381 self.targets.pop(path)
382 self.modified=True
383
384 ##
385 #
386 # une classe pour représenter un disque ou une partition.
387 #
388 # les attributs publics sont :
389 # - \b path le chemin dans le système dbus
390 # - \b device l'objet dbus qui correspond à l'instance
391 # - \b device_prop un proxy pour questionner cet objet dbus
392 # - \b selected booléen vrai si on doit considérer cette instance comme sélectionnée. Vrai à l'initialisation
393 # - \b rlock un verrou récursif permettant de réserver l'usage du media pour un seul thread
394 #
395 class uDisk2:
396
397 ##
398 #
399 # Le constructeur
400 # @param path un chemin comme '/org/freedesktop/UDisks2/block_devices/sdX'
401 # @param mp point de montage ('' par défaut)
402 # @param isUsb en général, vrai vu qu'on se s'intéressera qu'à des périphériques
403 # USB
404 # @param vendor indication de vendeur
405 # @param model indication de modèle
406 # @param parent périphérique parent (None par défaut)
407 # @param fstype type de système de fichiers
408 # @param serial numéro de série
409 # @param uuid identifiant donné au disque lors du formatage
410 # @param free taille de la zone libre pour l'écriture
411 # @param capacity taille du périphérique
412 # @param device pseudo-fichier pour l'accès au périphérique
413 # @param firstFat une instance de uDisk2, de type vfat parmi les partitions
414 # @param selected vrai/faux selon qu'on sélectionne ou non le périphérique (vrai par défaut)
415 #
416 def __init__(self, path, mp='', isUsb=False, vendor='', model='', parent=None,
417 fstype='', serial='', uuid='',
418 free=0, capacity=0, device='', firstFat=None, selected=True):
419 self.path=path
420 self.mp=mp
421 self.isUsb=isUsb
422 self.vendor=vendor
423 self.model=model
424 self.parent=parent
425 self.fstype=fstype
426 self.stickid=serial
427 self.uuid=uuid
428 self.free=free
429 self.capacity=capacity
430 self.devStuff=device
431 self.firstFat=firstFat
432 self.selected=selected
433 self.rlock=threading.RLock()
434 return
435
436 _itemNames={
437 "1mp":QApplication.translate("uDisk","point de montage",None),
438 "2capacity":QApplication.translate("uDisk","taille",None),
439 "3vendor":QApplication.translate("uDisk","marque",None),
440 "4model":QApplication.translate("uDisk","modèle de disque",None),
441 "5stickid":QApplication.translate("uDisk","numéro de série",None),
442 }
443
444 _specialItems={"0Check":QApplication.translate("uDisk","cocher",None)}
445
446 _ItemPattern=re.compile("[0-9]?(.*)")
447
448 ##
449 #
450 # renvoie un identifiant unique. Dans cette classe, cette fonction
451 # est synonyme de file()
452 # @return un identifiant unique, garanti par le système de fichiers
453 #
454 def uniqueId(self):
455 return self.file()
456
457 ##
458 #
459 # Méthode statique, pour avoir des titres de colonne.
460 # renvoie des titres pour les items obtenus par __getitem__.
461 # @param locale la locale, pour traduire les titres éventuellement.
462 # Valeur par défaut : "C"
463 # @return une liste de titres de colonnes
464 #
465 def headers(locale="C"):
466 result= list(uDisk2._specialItems.keys())+ list(uDisk2._itemNames.keys())
467 return sorted(result)
468
469 headers = staticmethod(headers)
470
471 ##
472 #
473 # Fournit une représentation imprimable
474 # @return une représentation imprimable de l'instance
475 #
476 def __str__(self):
477 return self.title()+self.valuableProperties()
478
479 ##
480 #
481 # Permet d'obtenir un identifiant unique de disque
482 # @return le chemin dbus de l'instance
483 #
484 def title(self):
485 return self.path
486
487 ##
488 #
489 # Permet de reconnaitre les partitions DOS-FAT
490 # @return True dans le cas d'une partition FAT16 ou FAT32
491 #
492 def isDosFat(self):
493 return self.fstype=="vfat"
494
495 ##
496 #
497 # @return True si le disque ou la partion est montée
498 #
499 def isMounted(self):
500 return bool(self.mp)
501
502 ##
503 #
504 # Facilite l'accès aux propriétés intéressantes d'une instance
505 # @return une chaîne indentée avec les propriétés intéressantes, une par ligne
506 #
507 def valuableProperties(self,indent=4):
508 prefix="\n"+" "*indent
509 r=""
510 props=["mp", "parent", "fstype", "stickid", "uuid", "vendor", "model", "devStuff", "free", "capacity"]
511 for prop in props:
512 r+=prefix+"%s = %s" %(prop, getattr(self,prop))
513 return r
514
515 ##
516 #
517 # @return le point de montage
518 #
519 def mountPoint(self):
520 return self.mp
521
522 ##
523 #
524 # retire le numéro des en-têtes pour en faire un nom de propriété
525 # valide pour interroger dbus
526 # @param n un numéro de propriété qui se réfère aux headers
527 # @return une propriété renvoyée par dbus, dans un format imprimable
528 #
529 def unNumberProp(self,n):
530 m=uDisk2._ItemPattern.match(self.headers()[n])
531 try:
532 return getattr(self, m.group(1))
533 except:
534 return ""
535
536 ##
537 #
538 # Renvoie un élément de listage de données internes au disque
539 # @param n un nombre
540 # @return un élément si n>0, et le drapeau self.selected si n==0.
541 # Les noms des éléments sont dans la liste itemNames utilisée dans
542 # la fonction statique headers
543 #
544 def __getitem__(self,n):
545 propListe=self.headers()
546 if n==0:
547 return self.selected
548 elif n <= len(propListe):
549 return self.unNumberProp(n-1)
550
551 ##
552 #
553 # Permet de s'assurer qu'une partition ou un disque sera bien monté
554 # @result le chemin du point de montage
555 #
556 def ensureMounted(self):
557 mount_paths=self.mp
558 if mount_paths==None: # le cas où la notion de montage est hors-sujet
559 return ""
560 leftTries=5
561 while len(mount_paths)==0 and leftTries >0:
562 leftTries = leftTries - 1
563 path=self.path
564 if len(path)>0:
565 subprocess.call("udisks --mount %s > /dev/null" %path,shell=True)
566 paths=self.mp
567 print("STILL TO DEBUG: is the mount OK? is self.mp updated?")
568 if paths:
569 return paths
570 else:
571 time.sleep(0.5)
572 else:
573 time.sleep(0.5)
574 if leftTries==0:
575 raise Exception ("Could not mount the VFAT after 5 tries.")
576 else:
577 return mount_paths
578
579
580
581 ##
582 #
583 # une classe pour représenter la collection des disques USB connectés
584 #
585 # les attributs publics sont :
586 # - \b access le type d'accès qu'on veut pour les items
587 # - \b targets la collection de disques USB, organisée en un dictionnaire
588 # de disques : les clés sont les disques, qui renvoient à un ensemble
589 # de partitions du disque
590 # - \b firstFats une liste composée de la première partion DOS-FAT de chaque disque USB.
591 #
592 class Available (UDisksBackend):
593
594 ##
595 #
596 # Le constructeur
597 # @param access définit le type d'accès souhaité. Par défaut, c'est "disk"
598 # c'est à dire qu'on veut la liste des disques USB. Autres valeurs
599 # possibles : "firstFat" pour les premières partitions vfat.
600 # @param diskClass la classe de disques à créer
601 #
602 def __init__(self, access="disk", diskClass=uDisk2):
603 UDisksBackend.__init__(self, diskClass=diskClass)
604 self.access=access
605 self.detect_devices()
606 self.finishInit()
607
608 ##
609 #
610 # Fin de l'initialisation
611 #
612 def finishInit(self):
613 self.mountFirstFats()
614
615 ##
616 #
617 # fabrique la liste des partitions FAT,
618 # monte les partitions FAT si elles ne le sont pas
619 #
620 def mountFirstFats(self):
621 self.firstFats = self.getFirstFats()
622 if self.access=="firstFat":
623 for p in self.firstFats:
624 uDisk2(p,self).ensureMounted()
625
626 ##
627 #
628 # @return le nombre de medias connectés
629 #
630 def __trunc__(self):
631 return len(self.firstFats)
632
633 ##
634 #
635 # Sert à comparer deux collections de disques, par exemple
636 # une collection passée et une collection présente.
637 # @param other une instance de Available
638 # @return vrai si other semble être la même collection de disques USB
639 #
640 def compare(self, other):
641 result=self.summary()==other.summary()
642 return result
643
644 ##
645 #
646 # Permet de déterminer si un disque est dans la collection
647 # @param ud une instance de uDisk
648 # @return vrai si le uDisk ud est dans la collection
649 #
650 def contains(self, ud):
651 return ud.path in self.targets
652
653 ##
654 #
655 # Récolte les enregistrements de niveau supérieur de self.targets
656 # @return la liste des chemins vers les disque USB détectés
657 #
658 def disks(self):
659 return [d for d in self.targets if not self.targets[d].parent]
660
661 ##
662 #
663 # Récolte les partitions d'un disque
664 # @param d le chemin vers un disque
665 # @return la liste des partitions de ce disque
666 #
667 def parts(self, d):
668 return [p for p in self.targets if self.targets[p].parent==d]
669
670 ##
671 #
672 # Récolte les enregistrements de niveau supérieur de self.targets
673 # @return la liste des objects uDisk2 détectés
674 #
675 def disks_ud(self):
676 return [self.targets[d] for d in self.targets if not self.targets[d].parent]
677
678 ##
679 #
680 # Récolte les partitions d'un disque
681 # @param d le chemin vers un disque
682 # @return la liste des objets uDisk2 qui sont des partitions
683 # de ce disque
684 #
685 def parts_ud(self, d):
686 return [self.targets[p] for p in self.targets if self.targets[p].parent==d]
687
688 ##
689 #
690 # Fournit une représentation imprimable d'un résumé
691 # @return une représentation imprimable d'un résumé de la collection
692 #
693 def summary(self):
694 r= "Available USB disks\n"
695 r+= "===================\n"
696 for d in sorted(self.disks()):
697 r+="%s\n" %(self.targets[d].devStuff)
698 partlist=self.parts(d)
699 if len(partlist)>0:
700 r+=" Partitions :\n"
701 for part in partlist:
702 r+=" %s\n" %(self.targets[part].devStuff,)
703 return r
704
705 ##
706 #
707 # Fournit une représentation imprimable
708 # @return une représentation imprimable de la collection
709 #
710 def __str__(self):
711 r= "Available USB disks\n"
712 r+= "===================\n"
713 for d in self.disks():
714 r+="%s\n" %d
715 partlist=self.parts(d)
716 if len(partlist)>0:
717 r+=" Partitions :\n"
718 for part in sorted(partlist):
719 r+=" %s\n" %(self.targets[part].devStuff)
720 r+=self.targets[part].valuableProperties(12)+"\n"
721 return r
722
723 ##
724 #
725 # Renvoye le nième disque. Le fonctionnement dépend du paramètre
726 # self.access
727 # @param n un numéro
728 # @return le nième disque USB connecté sous forme d'instance de uDisk2
729 #
730 def __getitem__(self, n):
731 if self.access=="disk":
732 path=self.targets.keys()[n]
733 elif self.access=="firstFat":
734 path=self.firstFats[n]
735 return self.targets[path]
736
737 ##
738 #
739 # Renseigne sur la longueur de la collection. Le fonctionnement
740 # dépend du paramètre self.access
741 # @return la longueur de la collection de disques renvoyée
742 #
743 def __len__(self):
744 if self.access=="disk":
745 return len(self.targets)
746 elif self.access=="firstFat":
747 return len(self.firstFats)
748
749 ##
750 #
751 # Facilite l'accès aux partitions de type DOS-FAT, et a des effets
752 # de bord :
753 # * marque la première vfat dans chaque instance de disque
754 # * construit une liste des chemins uDisk des FATs
755 # @return une liste de partitions, constituée de la première
756 # partition de type FAT de chaque disque USB connecté
757 #
758 def getFirstFats(self):
759 result=[]
760 disks=[d for d in self.targets if not self.targets[d].parent]
761 for d in disks:
762 parts=[p for p in self.targets if self.targets[p].parent==d]
763 for p in parts:
764 if self.targets[p].fstype=="vfat":
765 result.append(p)
766 # inscrit l'information dans l'instance du disque, par effet de bord
767 self.targets[d].firstFat=self.targets[p]
768 return result
769
770 ##
771 #
772 # @param dev un chemin comme /org/freedesktop/UDisks/devices/sdb3
773 # @return True si la partition est dans la liste des partions disponibles
774 #
775 def hasDev(self, dev):
776 s=str(dev)
777 for p in self.fatPaths:
778 if p.split("/")[-1]==s:
779 return True
780 return False
781
782 ##################### fin de la définition de la calsse uDisk2 ################
783
784
785
786 if __name__=="__main__":
787 from PyQt5.QtCore import *
788 from PyQt5.QtGui import *
789 import sys
790 class MainWindow(QMainWindow):
791 def __init__(self):
792 QMainWindow.__init__(self)
793
794 # The only thing in the app is a quit button
795 quitbutton = QPushButton('Examinez le terminal\nbranchez et débranchez des clés USB, puis\nQuittez', self)
796 quitbutton.clicked.connect(self.close)
797 self.setCentralWidget(quitbutton)
798
799
800 machin=Available()
801 print (machin)
802 def print_targets_if_modif(man, obj):
803 if machin.modified:
804 print([s.split("/")[-1] for s in machin.targets.keys()])
805 machin.modified=False
806 machin.addHook('object-added', print_targets_if_modif)
807 machin.addHook('object-removed', print_targets_if_modif)
808
809 app = QApplication(sys.argv)
810 main = MainWindow()
811 main.show()
812 sys.exit(app.exec_())
src.usbDisk2.uDisk2.uuid
uuid
Definition: usbDisk2.py:427
src.usbDisk2.UDisksBackend.modified
modified
self.modified signifie une modification récente, à prendre en compte par une application au niveau ut...
Definition: usbDisk2.py:128
src.usbDisk2.inspectData
def inspectData()
Definition: usbDisk2.py:36
src.usbDisk2.Available.access
access
Definition: usbDisk2.py:604
src.usbDisk2.uDisk2.ensureMounted
def ensureMounted(self)
Permet de s'assurer qu'une partition ou un disque sera bien monté
Definition: usbDisk2.py:556
src.usbDisk2.uDisk2
une classe pour représenter un disque ou une partition.
Definition: usbDisk2.py:395
QtCore
QtWidgets
src.usbDisk2.uDisk2.isDosFat
def isDosFat(self)
Permet de reconnaitre les partitions DOS-FAT.
Definition: usbDisk2.py:492
src.usbDisk2.Available.parts
def parts(self, d)
Récolte les partitions d'un disque.
Definition: usbDisk2.py:667
src.usbDisk2.uDisk2.__getitem__
def __getitem__(self, n)
Renvoie un élément de listage de données internes au disque.
Definition: usbDisk2.py:544
src.usbDisk2.uDisk2.headers
tuple headers
Definition: usbDisk2.py:469
src.usbDisk2.Available.__init__
def __init__
Le constructeur.
Definition: usbDisk2.py:602
src.usbDisk2.uDisk2.capacity
capacity
Definition: usbDisk2.py:429
src.usbDisk2.Available.hasDev
def hasDev(self, dev)
Definition: usbDisk2.py:775
src.usbDisk2.Available.__trunc__
def __trunc__(self)
Definition: usbDisk2.py:630
src.usbDisk2.UDisksBackend._udisks_partition_added
def _udisks_partition_added(self, obj, drive, partition)
Fonction de rappel pour l'ajout d'une partition, met à jour self.targets.
Definition: usbDisk2.py:290
src.usbDisk2.UDisksBackend.detect_devices
def detect_devices(self)
Fait un inventaire des disques.
Definition: usbDisk2.py:214
src.usbDisk2.UDisksBackend.install_thread
install_thread
Definition: usbDisk2.py:120
src.usbDisk2.Available.contains
def contains(self, ud)
Permet de déterminer si un disque est dans la collection.
Definition: usbDisk2.py:650
src.usbDisk2.uDisk2.__str__
def __str__(self)
Fournit une représentation imprimable.
Definition: usbDisk2.py:476
src.usbDisk2.uDisk2.parent
parent
Definition: usbDisk2.py:424
src.usbDisk2.UDisksBackend.manager
manager
Definition: usbDisk2.py:133
src.usbDisk2.uDisk2.__init__
def __init__
Le constructeur.
Definition: usbDisk2.py:418
src.usbDisk2.MainWindow.__init__
def __init__(self)
Definition: usbDisk2.py:791
src.usbDisk2.Available.parts_ud
def parts_ud(self, d)
Récolte les partitions d'un disque.
Definition: usbDisk2.py:685
src.usbDisk2.uDisk2.mp
mp
Definition: usbDisk2.py:420
src.usbDisk2.UDisksBackend.cbHooks
cbHooks
Definition: usbDisk2.py:134
src.usbDisk2.uDisk2.mountPoint
def mountPoint(self)
Definition: usbDisk2.py:519
src.usbDisk2.UDisksBackend._udisks_drive_added
def _udisks_drive_added(self, obj, drive, part)
Definition: usbDisk2.py:333
src.usbDisk2.UDisksBackend.__init__
def __init__
Le constructeur.
Definition: usbDisk2.py:119
src.usbDisk2.uDisk2.stickid
stickid
Definition: usbDisk2.py:426
src.usbDisk2.uDisk2.isMounted
def isMounted(self)
Definition: usbDisk2.py:499
src.usbDisk2.uDisk2.isUsb
isUsb
Definition: usbDisk2.py:421
src.usbDisk2.Available.disks
def disks(self)
Récolte les enregistrements de niveau supérieur de self.targets.
Definition: usbDisk2.py:658
src.usbDisk2.uDisk2.devStuff
devStuff
Definition: usbDisk2.py:430
src.usbDisk2.uDisk2.rlock
rlock
Definition: usbDisk2.py:433
src.usbDisk2.uDisk2.model
model
Definition: usbDisk2.py:423
src.usbDisk2.Available.__len__
def __len__(self)
Renseigne sur la longueur de la collection.
Definition: usbDisk2.py:743
src.usbDisk2.UDisksBackend.bus
bus
Definition: usbDisk2.py:131
src.usbDisk2.UDisksBackend.diskClass
diskClass
self.targets est un dictionnaire des disques détectés les clés sont les paths et les contenus des ins...
Definition: usbDisk2.py:124
src.usbDisk2.UDisksBackend._interesting_obj
def _interesting_obj(self, obj)
trouve si un objet est intéressant à cataloguer
Definition: usbDisk2.py:225
src.usbDisk2.uDisk2.free
free
Definition: usbDisk2.py:428
src.usbDisk2.safePath
def safePath(obj)
Récupère de façon sûre le path d'une instance de UDisksObjectProxy.
Definition: usbDisk2.py:60
src.usbDisk2.print_targets_if_modif
def print_targets_if_modif(man, obj)
Definition: usbDisk2.py:802
src.usbDisk2.fs_size
def fs_size(device)
Renvoie la taille d'un système de fichier et la place disponible.
Definition: usbDisk2.py:76
src.usbDisk2.Available.getFirstFats
def getFirstFats(self)
Facilite l'accès aux partitions de type DOS-FAT, et a des effets de bord :
Definition: usbDisk2.py:758
src.usbDisk2.UDisksBackend._device_changed
def _device_changed(self, obj)
Definition: usbDisk2.py:367
src.usbDisk2.Available.summary
def summary(self)
Fournit une représentation imprimable d'un résumé
Definition: usbDisk2.py:693
src.usbDisk2.uDisk2.path
path
Definition: usbDisk2.py:419
src.usbDisk2.UDisksBackend._udisks_obj_removed
def _udisks_obj_removed(self, obj)
Fonction de rappel déclenchée par le retrait d'un disque.
Definition: usbDisk2.py:377
src.usbDisk2.UDisksBackend._udisks_obj_added
def _udisks_obj_added(self, obj)
Fonction de rappel pour les ajouts de disque.
Definition: usbDisk2.py:262
src.usbDisk2.MainWindow
Definition: usbDisk2.py:790
QMainWindow
src.usbDisk2.Available.firstFats
firstFats
Definition: usbDisk2.py:621
src.usbDisk2.Available
une classe pour représenter la collection des disques USB connectés
Definition: usbDisk2.py:592
src.usbDisk2.UDisksBackend.retry_mount
def retry_mount
Essaie de monter un système de fichier jusqu'à ce qu'il cesse d'échouer avec "Busy", ou que l'erreur soit "déjà monté".
Definition: usbDisk2.py:194
src.usbDisk2.UDisksBackend.addHook
def addHook(self, signal, func)
ajoute une fonction à appeler pour un signal nommé, et enregistre cette fonction dans self...
Definition: usbDisk2.py:177
src.usbDisk2.Available.finishInit
def finishInit(self)
Fin de l'initialisation.
Definition: usbDisk2.py:612
src.usbDisk2.uDisk2.fstype
fstype
Definition: usbDisk2.py:425
src.usbDisk2.Available.__str__
def __str__(self)
Fournit une représentation imprimable.
Definition: usbDisk2.py:710
src.usbDisk2.Available.__getitem__
def __getitem__(self, n)
Renvoye le nième disque.
Definition: usbDisk2.py:730
src.usbDisk2.uDisk2.firstFat
firstFat
Definition: usbDisk2.py:431
src.usbDisk2.uDisk2.title
def title(self)
Permet d'obtenir un identifiant unique de disque.
Definition: usbDisk2.py:484
src.usbDisk2.Available.compare
def compare(self, other)
Sert à comparer deux collections de disques, par exemple une collection passée et une collection prés...
Definition: usbDisk2.py:640
src.usbDisk2.UDisksBackend.objIsUsb
def objIsUsb(self, obj)
détermine si un périphérique est de type USB
Definition: usbDisk2.py:276
src.usbDisk2.uDisk2.selected
selected
Definition: usbDisk2.py:432
src.usbDisk2.uDisk2.unNumberProp
def unNumberProp(self, n)
retire le numéro des en-têtes pour en faire un nom de propriété valide pour interroger dbus ...
Definition: usbDisk2.py:529
src.usbDisk2.UDisksBackend.targets
targets
Definition: usbDisk2.py:125
src.usbDisk2.UDisksBackend
Cette classe a été inspirée par le projet USBcreator.
Definition: usbDisk2.py:111
src.usbDisk2.Available.disks_ud
def disks_ud(self)
Récolte les enregistrements de niveau supérieur de self.targets.
Definition: usbDisk2.py:675
src.usbDisk2.Available.mountFirstFats
def mountFirstFats(self)
fabrique la liste des partitions FAT, monte les partitions FAT si elles ne le sont pas ...
Definition: usbDisk2.py:620
src.usbDisk2.uDisk2.vendor
vendor
Definition: usbDisk2.py:422
QtGui
src.usbDisk2.uDisk2.valuableProperties
def valuableProperties
Facilite l'accès aux propriétés intéressantes d'une instance.
Definition: usbDisk2.py:507
src.usbDisk2.UDisksBackend.logger
logger
Definition: usbDisk2.py:121
src.usbDisk2.UDisksBackend.udisks
udisks
Definition: usbDisk2.py:132
src.usbDisk2.uDisk2.uniqueId
def uniqueId(self)
renvoie un identifiant unique.
Definition: usbDisk2.py:454