Code Vonc

SauVonc

v 1.0

Win

TéléchargerDownload
Faire un donMake a donation
CommentairesComments

SauVonc permet de rendre plusieurs dossiers ou disques durs identiques en copiant les données entre eux.

Fichier .exe compilé disponible pour Windows.

Interface avec WX.



Fonctionnalités

- Pas de limite de dossiers à comparer.
- Compare par date et taille.
- Un dossier marqué comme Référence permet aux autres dossiers d'ajuster leurs contenus en fonction de celui-ci.


Codes sourcesSources code

SauVonc.py

import os, wx, shutil, ConfigParser, thread, types

class Fenetre(wx.Frame) :
	TxtDescription = u"Affiche les éléments à copier pour obtenir des dossiers identiques."
	TxtInfos = "v1.0 - César Vonc - http://code.vonc.fr".decode('UTF-8', 'strict')
	TxtAjouterRep = "+ Ajouter un répertoire".decode('UTF-8', 'strict')
	TxtComparer = "Comparer"
	TxtAnnuler = "Arrêter".decode('UTF-8', 'strict')
	TxtSousDossiers = "Inclure les sous-dossiers".decode('UTF-8', 'strict')
	TxtDate = u"Comparer la date (nécessite un dossier de ref.)"
	TxtTaille = u"Comparer la taille (nécessite un dossier de ref.)"
	TxtDosRef = u"Référence"
	TxtRepInval = u"Répertoire inexistant"
	TxtNbFichier = u" à copier"
	TxtNbSuppr = u" à virer"
	TxtFermer = "Fermer"
	TxtRef = u"Dossier de référence"
	TxtParcourir = "Parcourir"
	TxtToutSel = "Tout sélectionner".decode('UTF-8', 'strict')
	TxtToutDesel = "Tout déselectionner".decode('UTF-8', 'strict')
	TxtCopier = "Copier"
	TxtSuppr = "Supprimer"
	reps = []
	annuler = False
	traitement = False
	i = 0
	
	def __init__(self, parent, id, title) :
		wx.Frame.__init__(self, parent, id, title, size = [800, 600])
		
		icone = wx.Icon("sauvonc.ico", wx.BITMAP_TYPE_ICO)
		self.SetIcon(icone)
		
		self.panneau = wx.Panel(self, -1)
		self.boite = wx.GridBagSizer()
		
		self.texte = wx.StaticText(self.panneau, -1, self.TxtDescription)
		self.boite.Add(self.texte, (0,0), (1,2))
		self.texte2 = wx.StaticText(self.panneau, -1, self.TxtInfos)
		self.boite.Add(self.texte2, (0,2), (1,4), wx.ALIGN_RIGHT)
		
		self.ajouterrep = wx.Button(self.panneau, label = self.TxtAjouterRep)
		self.ajouterrep.Bind(wx.EVT_BUTTON, self.eveAjouterRep)
		self.boite.Add(self.ajouterrep, (2,0), (1,1), wx.EXPAND)
		self.sousdossiers = wx.CheckBox(self.panneau, -1, self.TxtSousDossiers)
		self.sousdossiers.Bind(wx.EVT_CHECKBOX, self.eveSousDossier)
		self.boite.Add(self.sousdossiers, (2,1), (1,1), wx.ALIGN_CENTER)
		self.comparer = wx.Button(self.panneau, label = self.TxtComparer)
		self.comparer.SetBackgroundColour(wx.Colour(0, 255, 128))
		self.comparer.Bind(wx.EVT_BUTTON, self.eveBtnComparer)
		self.boite.Add(self.comparer, (2,2), (1,1), wx.EXPAND)
		self.charge = wx.Gauge(self.panneau, size = [100, 10])
		self.boite.Add(self.charge, (2,3), (1,1), wx.EXPAND)
		self.meta = wx.BoxSizer(wx.VERTICAL)
		self.boite.Add(self.meta, (2,4), (1,2), wx.EXPAND)
		self.date = wx.CheckBox(self.panneau, -1, self.TxtDate)
		self.meta.Add(self.date)
		self.date.Bind(wx.EVT_CHECKBOX, self.eveMeta)
		self.taille = wx.CheckBox(self.panneau, -1, self.TxtTaille)
		self.meta.Add(self.taille)
		self.taille.Bind(wx.EVT_CHECKBOX, self.eveMeta)
		
		self.grille = wx.GridSizer(hgap = 10)
		self.boite.Add(self.grille, (4,0), (1,6), wx.EXPAND)
		
		repertoires, ref = self.FichierConfig()
		if len(repertoires) == 0 :
			self.AjRep(self.panneau, self.grille, "C:\\Documents\\")
			self.AjRep(self.panneau, self.grille, "D:\\Documents\\")
		else :
			k = 0
			for rep in repertoires :
				if ref == k : refere = True
				else : refere = False
				self.AjRep(self.panneau, self.grille, rep, refere)
				k += 1
		
		self.boite.AddGrowableCol(5)
		self.boite.AddGrowableRow(4)
		
		self.panneau.SetSizer(self.boite)
		self.MajChemins()
		self.FichierConfig(False)
		self.Recadre()
		self.Show(True)
		
		#self.tete = Tete(self.Compare)
		#self.tete.start()
	
	def AjRep(self, panneau, grille, dossier, refere = False) :
		rep = [] # 0=btn, 1=chemin, 2=liste, 3=EnsembleListe, 4=Valide, 5=ID, 6=Case, 7=nbfic, 8=cheminBtn, 9=ref, 10=listeS, 11=EnsembleListeSuppr
		case = wx.GridBagSizer()
		
		# Bouton Fermer
		btn = wx.Button(panneau, label = self.TxtFermer, name = str(self.i))
		nbfic = wx.StaticText(panneau, -1, "")
		btn.Bind(wx.EVT_BUTTON, self.eveBtnFermer)
		case.Add(nbfic, (0,0), (1,1), wx.ALIGN_CENTER)
		case.Add(btn, (0,1), (1,1), wx.ALIGN_RIGHT)
		rep.append(btn)
		
		#
		ref = wx.CheckBox(panneau, -1, self.TxtRef, name = str(self.i))
		ref.Bind(wx.EVT_CHECKBOX, self.eveBtnRef)
		ref.Value = refere
		case.Add(ref, (1,0), (1,2))
		
		# Texte Chemin
		chemin = wx.TextCtrl(panneau, -1, dossier, style = wx.TE_PROCESS_ENTER|wx.TE_RICH2)
		chemin.Bind(wx.EVT_TEXT_ENTER, self.eveChampTexte)
		case.Add(chemin, (2,0), (1,1), wx.EXPAND)
		rep.append(chemin)
		
		cheminBtn = wx.Button(panneau, label = self.TxtParcourir, name = str(self.i))
		cheminBtn.Bind(wx.EVT_BUTTON, self.eveBtnChemin)
		case.Add(cheminBtn, (2,1), (1,1), wx.EXPAND)
		
		# Champ Liste
		liste = wx.ListBox(panneau, -1, choices = [], style = wx.LB_MULTIPLE|wx.LB_HSCROLL)
		liste.SetMinSize((10, 10))
		case.Add(liste, (3,0), (1,2), wx.EXPAND)
		rep.append(liste)
		
		# Ensemble
		rep.append(set())
		
		# Valide
		rep.append(False)
		
		# ID
		rep.append(self.i)
		
		# Case
		rep.append(case)
		
		# Nb fichiers manquants
		rep.append(nbfic)
		
		# Parcourir
		rep.append(cheminBtn)
		
		# Ref
		rep.append(ref)
		
		# Tout Sélectionner
		toutSel = wx.Button(panneau, label = self.TxtToutSel, name = str(self.i))
		toutSel.tor = True
		toutSel.Bind(wx.EVT_BUTTON, self.eveBtnSel)
		case.Add(toutSel, (4,0), (1,1))
		
		# Copier
		copier = wx.Button(panneau, label = self.TxtCopier, name = str(self.i))
		copier.Bind(wx.EVT_BUTTON, self.eveBtnCopier)
		case.Add(copier, (4,1), (1,1), wx.ALIGN_RIGHT)
		
		# Champ Liste Suppr
		listeS = wx.ListBox(panneau, -1, choices = [], style = wx.LB_MULTIPLE|wx.LB_HSCROLL)
		listeS.SetMinSize((10, 10))
		case.Add(listeS, (5,0), (1,2), wx.EXPAND)
		rep.append(listeS)
		rep.append(set())
		
		# Tout Sélectionner Suppr
		toutSelS = wx.Button(panneau, label = self.TxtToutSel, name = str(self.i))
		toutSelS.tor = True
		toutSelS.Bind(wx.EVT_BUTTON, self.eveBtnSelS)
		case.Add(toutSelS, (6,0), (1,1))
		
		# Supprimer
		suppr = wx.Button(panneau, label = self.TxtSuppr, name = str(self.i))
		suppr.Bind(wx.EVT_BUTTON, self.eveBtnSuppr)
		case.Add(suppr, (6,1), (1,1), wx.ALIGN_RIGHT)
		
		# --------------
		case.AddGrowableCol(0)
		case.AddGrowableRow(3)
		case.AddGrowableRow(5)
		grille.Add(case, 0, wx.EXPAND)
		self.reps.append(rep)
		self.i += 1
	
	def trouveRef(self) :
		for r in self.reps :
			if r[9].Value is True :
				return r[1].Value
		return None
	
	def trouveFenParNom(self, n) :
		k = 0
		for r in self.reps :
			if r[5] == n :
				return r
			k += 1
		return None
	
	def copier(self, cible, source) :
		max = len(cible)
		if max != len(source) : return
		
		for f in xrange(max) :
			src = source[f]
			cib = cible[f]
			if src is None : continue
			if os.path.isdir(src) :
				os.makedirs(cib)
			else :
				dos = os.path.split(cib)[0]
				if not os.path.exists(dos) :
					os.makedirs(dos)
				try : shutil.copy2(src, cib)
				except EnvironmentError, e : print e
			
			self.charge.SetValue(f * 100 / max)
	
	def virer(self, source) :
		max = len(source)
		for f in xrange(max) :
			src = source[f]
			if os.path.exists(src) :
				if os.path.isdir(src) :
					try : shutil.rmtree(src)
					except EnvironmentError, e : print e
				else :
					try : os.remove(src)
					except EnvironmentError, e : print e
			self.charge.SetValue(f * 100 / max)
	
	def Cheminer(self, chemDef) :
		fenChem = wx.DirDialog(None, defaultPath = chemDef)
		if fenChem.ShowModal() == wx.ID_OK :
			return fenChem.GetPath()
	
	def eveMeta(self, eve) :
		if self.traitement is True : return
		chemRef = self.trouveRef()
		if chemRef is None :
			eve.EventObject.SetValue(False)
			return
		self.Initialise()
	
	def eveBtnRef(self, eve) :
		if self.traitement is True : return
		n = int(eve.EventObject.Name)
		for r in self.reps :
			if r[5] != n :
				r[9].SetValue(False)
			else : val = r[9].GetValue()
		self.Initialise(not val)
	
	def eveBtnChemin(self, eve) :
		if self.traitement is True : return
		n = int(eve.EventObject.Name)
		r = self.trouveFenParNom(n)
		chem = self.Cheminer(r[1].Value)
		if chem : r[1].Value = chem
		
		self.MajChemins()
		self.FichierConfig(False)
	
	def eveBtnComparer(self, eve) :	
		self.MajChemins()
		self.FichierConfig(False)
		self.Compare()
		self.Recadre()
	
	def eveBtnCopier(self, eve) :
		if self.traitement is True : return
		#self.traitement = True
		n = int(eve.EventObject.Name)
		r = self.trouveFenParNom(n)
		if r is None : return
		
		sel = r[2].GetSelections()
		if len(sel) == 0 : return
		chem = r[1].Value
		sel = [r[3][s] for s in sel]
		
		chemRef = self.trouveRef()
		
		cible = [os.path.join(chem, s) for s in sel]
		source = []
		for s in sel :
			fichier = None
			if chemRef is None :
				for r in self.reps :
					fic = os.path.join(r[1].Value, s)
					if os.path.exists(fic) :
						fichier = fic
						break
			else :
				fichier = os.path.join(chemRef, s)
			source.append(fichier)
		
		self.copier(cible, source)
		
		#self.traitement = False
		self.Compare()
		self.Recadre()
	
	
	def eveBtnSuppr(self, eve) :
		if self.traitement is True : return
		#self.traitement = True
		n = int(eve.EventObject.Name)
		r = self.trouveFenParNom(n)
		if r is None : return
		
		sel = r[10].GetSelections()
		if len(sel) == 0 : return
		chem = r[1].Value
		sel = [os.path.join(chem, r[11][s]) for s in sel]
		
		self.virer(sel)
		
		#self.traitement = False
		self.Compare()
		self.Recadre()
	
	def eveBtnSel(self, eve) :
		if self.traitement is True : return
		btnSel = eve.EventObject
		n = int(btnSel.Name)
		r = self.trouveFenParNom(n)
		if r is None : return
		if btnSel.tor is True :
			btnSel.tor = False
			btnSel.Label = self.TxtToutDesel
			for i in xrange(len(r[3])) :
				r[2].SetSelection(i, True)
		else :
			btnSel.tor = True
			btnSel.Label = self.TxtToutSel
			r[2].DeselectAll()
	
	def eveBtnSelS(self, eve) :
		if self.traitement is True : return
		btnSel = eve.EventObject
		n = int(btnSel.Name)
		r = self.trouveFenParNom(n)
		if r is None : return
		if btnSel.tor is True :
			btnSel.tor = False
			btnSel.Label = self.TxtToutDesel
			for i in xrange(len(r[11])) :
				r[10].SetSelection(i, True)
		else :
			btnSel.tor = True
			btnSel.Label = self.TxtToutSel
			r[10].DeselectAll()
	
	def eveSousDossier(self, eve) :
		if self.traitement is True : return
		self.Initialise()
		#self.FichierConfig(False)
		#self.Compare()
		#self.Recadre()
	
	def eveAjouterRep(self, eve) :
		if self.traitement is True : return
		self.Initialise()
		self.AjRep(self.panneau, self.grille, "")
		self.Recadre()
	
	def eveBtnFermer(self, eve) :
		if self.traitement is True : return
		
		btn = eve.EventObject
		n = int(btn.Name)
		k = 0
		for r in self.reps :
			if r[5] == n :
				self.grille.Hide(r[6])
				self.grille.Remove(r[6])
				self.grille.Layout()
				self.Refresh()
				val = r[9].GetValue()
				break
			k += 1
		self.reps.pop(k)
		self.Initialise(val)
		self.Recadre()
	
	def eveChampTexte(self, eve) :
		if self.traitement is True : return
		
		self.MajChemins()
		self.FichierConfig(False)
		self.Compare()
		self.Recadre()
	
	def MajChemins(self) :
		for r in self.reps :
			chemin = r[1].Value
			if chemin and chemin[-1:] != "\\" : chemin += "\\"
			if not chemin or not os.path.exists(chemin) or not os.path.isdir(chemin) :
				#r[2].DeleteAllItems()
				r[2].Set([])
				r[2].Refresh()
				r[7].Label = self.TxtRepInval
				r[4] = False
				continue
			r[4] = True
			r[1].Value = chemin
	
	def Initialise(self, meta = False) :
		for r in self.reps :
			r[3] = []
			r[11] = []
			r[2].Set([])
			r[10].Set([])
			r[7].SetBackgroundColour(wx.NullColor)
			if r[4] is False : continue
			r[7].Label = ""
		if meta is True :
			self.date.SetValue(False)
			self.taille.SetValue(False)
	
	def Parcourir(self, chemin) :
		sd = self.sousdossiers.GetValue()
		try : contenu = os.listdir(chemin)
		except EnvironmentError, e :
			print e
			return [], None
		
		compare = None
		date = self.date.Value
		taille = self.taille.Value
		meta = False
		if date is True or taille is True :
			meta = True
			compare = []
		
		# Sans sous-dossiers
		if sd is False : 
			for i in xrange(len(contenu)) :
				fic = os.path.join(chemin, contenu[i])
				if os.path.isdir(fic) :
					contenu[i] += "\\"
					if meta is True : compare.append(contenu[i])
				else :
					if meta is True :
						ma = ""
						if date is True : ma += str(os.path.getmtime(fic)) + "-"
						if taille is True : ma += str(os.path.getsize(fic)) + "-"
						compare.append(contenu[i] + "||" + ma)
			return contenu, compare
		
		# Avec sous-dossiers
		j = 0
		for f in contenu :
			fa = os.path.join(chemin, f)
			if os.path.isdir(fa) :
				if self.annuler is True : break
				contenu[j] += "\\"
				try : dos = os.listdir(fa)
				except EnvironmentError, e :
					print e
					continue
				fs = [os.path.join(f, sf) for sf in dos]
				contenu.extend(fs)
				if meta is True : compare.append(contenu[j])
				self.charge.Pulse()
			else :
				if meta is True :
					ma = ""
					if date is True : ma += str(os.path.getmtime(fa)) + "-"
					if taille is True : ma += str(os.path.getsize(fa)) + "-"
					compare.append(contenu[j] + "||" + ma)
			j += 1
		return contenu, compare
	
	def Traiteur(self, traite) :
		if traite is True :
			self.comparer.Label = self.TxtAnnuler
			self.comparer.SetBackgroundColour(wx.Colour(255, 0, 128))
			self.traitement = True
		else :
			self.charge.SetValue(1)
			self.charge.SetValue(0)
			self.comparer.Label = self.TxtComparer
			self.comparer.SetBackgroundColour(wx.Colour(0, 255, 128))
			self.traitement = False
	
	def Compare(self) :
		self.annuler = False
		if self.traitement is False :
			thread.start_new_thread(self.Comparer, ())
		else : self.annuler = True
	
	def Comparer(self) :
		self.Traiteur(True)
		self.Initialise()
		reps = self.reps
		date = self.date.Value
		taille = self.taille.Value
		meta = False
		if date is True or taille is True :
			meta = True
		# Recherche Ref
		ref = None
		for r in reps : 
			if r[4] is False : continue
			if r[9].Value is True :
				ref = r[5]
				contenu, compare = self.Parcourir(r[1].Value)
				if compare is None : compare = contenu
				r_ref = set(compare)
				r[2].Set([])
				r[7].Label = self.TxtDosRef
				r[7].SetBackgroundColour(wx.WHITE)
				break
		
		r_tot = set()
		r_com = set()
		c = 1
		
		# -- Sans Ref
		if ref is None : 
			for r in reps :
				if r[4] is False : continue
				chemin = r[1].Value
				contenu, compare = self.Parcourir(chemin)
				if compare is None : compare = contenu
				if self.annuler is True :
					self.Traiteur(False)
					return
				r[3] = set(compare)
				r_tot = r_tot | r[3]
			
			for r in reps :
				if r[4] is False : continue
				r_dif = r_tot - r[3]
				r_com = r_com | r_dif
				li = list(r_dif)
				if meta is True : li = [m.split("||")[0] for m in li]
				li.sort()
				
				self.charge.SetValue(c * 100 / len(reps))
				r[3] = li
				r[7].Label = str(len(li)) + self.TxtNbFichier
				r[2].Set(li)
				c += 1
				#r[6].Refresh()
				
		# -- Avec Ref
		else :
			for r in reps :
				if r[4] is False : continue
				if r[9].Value is True : continue
				contenu, compare = self.Parcourir(r[1].Value)
				if self.annuler is True :
					self.Traiteur(False)
					return
				if compare is None : compare = contenu
				r_dif = r_ref - set(compare)
				r_trop = set(compare) - r_ref
				li = list(r_dif)
				if meta is True : li = [m.split("||")[0] for m in li]
				li.sort()
				r[3] = li
				liS = list(r_trop)
				if meta is True : liS = [m.split("||")[0] for m in liS]
				liS.sort()
				r[11] = liS
				r[2].Set(li)
				r[10].Set(liS)
				r[7].Label = str(len(li)) + self.TxtNbFichier + ", " + str(len(liS)) + self.TxtNbSuppr
				self.charge.SetValue(c * 100 / len(reps))
				c += 1
		
		self.panneau.Layout()
		self.Traiteur(False)
	
	def Recadre(self) :
		self.boite.Layout()
		self.panneau.Layout()
		self.Refresh()
	
	def FichierConfig(self, lire = True) :
		config = ConfigParser.RawConfigParser()
		nom = 'config.cfg'
		if lire is True :
			liste_rep = []
			ref = False
			if not os.path.exists(nom) : return liste_rep, ref
			config.read(nom)
			nb = config.getint('Dossiers', 'nombre')
			sdos = config.get('Dossiers', 'sousdossiers')
			if sdos == "Oui" : self.sousdossiers.SetValue(True)
			else : self.sousdossiers.SetValue(False)
			for i in xrange(nb) :
				rep = config.get('Dossiers', 'rep'+str(i))
				if rep : liste_rep.append(rep.decode('UTF-8', 'strict'))
			ref = config.get('Dossiers', 'ref')
			if str(ref) == "Non" : ref = None
			else : ref = int(ref)
			return liste_rep, ref
		else :
			ref = "Non"
			sdos = self.sousdossiers.GetValue()
			if sdos : sdos = "Oui"
			else : sdos = "Non"
			config.add_section('Dossiers')
			config.set('Dossiers', 'nombre', len(self.reps))
			config.set('Dossiers', 'sousdossiers', sdos)
			for i in xrange(len(self.reps)) :
				chemin = self.reps[i][1].Value
				config.set('Dossiers', 'rep'+str(i), chemin.encode('UTF-8', 'strict'))
				if self.reps[i][9].Value is True : ref = i
			config.set('Dossiers', 'ref', ref)
			with open(nom, 'wb') as fichier:
				config.write(fichier)
	
def main() :
	lec = wx.App()
	fenetre = Fenetre(None, -1, "SauVonc")
	lec.MainLoop()

if __name__ == "__main__" :
	main()