Pellicule galerie + délai S_PREV + USB unbind/bind

- draw_galerie() : rendu pellicule (bande film avec perforations) quand le
  mode est actif
- _load_gal() : charge les surfs à la taille des cadres pellicule si actif
- S_PREV : 1.5s → 2.5s pour laisser le DSLR revenir en live view
- _usb_reset_done() : utilise unbind/bind sysfs au lieu de usbreset
  (unbind/bind = seule méthode qui libère réellement le claim USB)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-11 17:43:10 +02:00
parent 0a90ae1de6
commit 5fc2f11bd4

View File

@@ -93,7 +93,7 @@ THEMES = {
# ── Utilitaire USB ─────────────────────────────────────────────────────────── # ── Utilitaire USB ───────────────────────────────────────────────────────────
def _usb_reset_done(): def _usb_reset_done():
"""Reset USB des DSLR Canon/Nikon/Sony pour libérer tout claim obsolète.""" """Reset USB des DSLR via unbind/bind sysfs pour libérer tout claim obsolète."""
import glob, subprocess as _sp import glob, subprocess as _sp
for dev_path in glob.glob('/sys/bus/usb/devices/[0-9]*-[0-9.]*'): for dev_path in glob.glob('/sys/bus/usb/devices/[0-9]*-[0-9.]*'):
prod_file = f"{dev_path}/product" prod_file = f"{dev_path}/product"
@@ -106,12 +106,14 @@ def _usb_reset_done():
if not any(b in prod for b in ('Canon', 'Nikon', 'Sony', 'Digital Camera')): if not any(b in prod for b in ('Canon', 'Nikon', 'Sony', 'Digital Camera')):
continue continue
try: try:
bus = int(open(f"{dev_path}/busnum").read().strip()) dev_name = os.path.basename(dev_path)
dev = int(open(f"{dev_path}/devnum").read().strip()) _sp.run(['sudo', 'sh', '-c', f'echo {dev_name} > /sys/bus/usb/drivers/usb/unbind'],
usb_path = f"/dev/bus/usb/{bus:03d}/{dev:03d}" capture_output=True, timeout=5)
_sp.run(['sudo', 'usbreset', usb_path], capture_output=True, timeout=5) time.sleep(0.8)
print(f"USB reset: {usb_path} ({prod})") _sp.run(['sudo', 'sh', '-c', f'echo {dev_name} > /sys/bus/usb/drivers/usb/bind'],
time.sleep(1.2) capture_output=True, timeout=5)
print(f"USB unbind/bind: {dev_name} ({prod})")
time.sleep(2.0)
except Exception as e: except Exception as e:
print(f"USB reset err: {e}") print(f"USB reset err: {e}")
@@ -836,7 +838,7 @@ class PB:
thumb_h = _PELL_FH if is_pell else 200 # haute résolution en mode pellicule thumb_h = _PELL_FH if is_pell else 200 # haute résolution en mode pellicule
for i in range(nb): for i in range(nb):
self.num_ph=i; self.etat=S_PREV; time.sleep(1.5) self.num_ph=i; self.etat=S_PREV; time.sleep(2.5)
self.etat=S_COMPTE self.etat=S_COMPTE
for c in range(delai,0,-1): self.chiffre=c; time.sleep(1) for c in range(delai,0,-1): self.chiffre=c; time.sleep(1)
self.etat=S_CAP; ch=self._capturer() self.etat=S_CAP; ch=self._capturer()
@@ -858,7 +860,16 @@ class PB:
def _load_gal(self): def _load_gal(self):
surfs = [] surfs = []
if self.format in ("bande3x2","bande4x2"): is_pell = self.cfg.get("fonctionnalites",{}).get("pellicule_mode",False)
if is_pell and self.format in ("bande3x2","bande4x2"):
nb = 3 if self.format=="bande3x2" else 4
fw = (W - _PELL_GAP*(nb+1)) // nb
for ch in self.photos[:nb]:
try:
img=ImageOps.exif_transpose(Image.open(ch).convert("RGB")).resize((fw,_PELL_FH),Image.LANCZOS)
surfs.append(pygame.image.fromstring(img.tobytes(),img.size,"RGB"))
except: surfs.append(None)
elif self.format in ("bande3x2","bande4x2"):
nb = 3 if self.format=="bande3x2" else 4 nb = 3 if self.format=="bande3x2" else 4
pw = W // nb; sh = (H - 150) // 2 pw = W // nb; sh = (H - 150) // 2
for ch in self.photos[:nb]: for ch in self.photos[:nb]:
@@ -1319,6 +1330,34 @@ class PB:
for k in range(4): for k in range(4):
a=int(128+127*math.sin(t*3+k*1.5)) a=int(128+127*math.sin(t*3+k*1.5))
pygame.draw.circle(self.scr,(*T["prim"],a),(W//2-60+k*40,H//2+30),12) pygame.draw.circle(self.scr,(*T["prim"],a),(W//2-60+k*40,H//2+30),12)
elif self._is_pell():
# ── Galerie style pellicule ──
nb=3 if self.format=="bande3x2" else 4
gap=_PELL_GAP; fw=(W-gap*(nb+1))//nb; fh=_PELL_FH
film_y=(H-_FILM_H)//2; frame_y=film_y+_SPR_H+_SPR_GAP
self.scr.fill((8,8,8))
pygame.draw.rect(self.scr,(18,18,18),(0,film_y,W,_FILM_H))
pygame.draw.rect(self.scr,(35,35,35),(0,film_y,W,_FILM_H),1)
hole_w,hole_h=32,22
for hy in (film_y+_SPR_H//2-hole_h//2, film_y+_FILM_H-_SPR_H//2-hole_h//2):
x=18
while x+hole_w<W-10:
pygame.draw.rect(self.scr,(180,180,180),(x,hy,hole_w,hole_h),border_radius=5)
pygame.draw.rect(self.scr,(8,8,8),(x+3,hy+3,hole_w-6,hole_h-6),border_radius=3)
x+=62
for i in range(nb):
fx=gap+i*(fw+gap); fy=frame_y
surf=self.gal_surfs[i] if i<len(self.gal_surfs) else None
if surf:
ts=pygame.transform.scale(surf,(fw,fh))
self.scr.blit(ts,(fx,fy))
pygame.draw.rect(self.scr,T["prim"],(fx,fy,fw,fh),2)
else:
pygame.draw.rect(self.scr,(15,15,15),(fx,fy,fw,fh))
nf=self.F["xxs"].render(f"{i+1:02d}A",True,(90,90,90))
num_y=frame_y+fh+_SPR_GAP+4
if num_y+20<film_y+_FILM_H:
self.scr.blit(nf,(fx+fw//2-nf.get_width()//2,num_y))
elif self.format in ("bande3x2","bande4x2"): elif self.format in ("bande3x2","bande4x2"):
nb=3 if self.format=="bande3x2" else 4 nb=3 if self.format=="bande3x2" else 4
pw=W//nb; sh=(H-150)//2 pw=W//nb; sh=(H-150)//2