3.0.0: OrganiLAB pasa a ser un programa de Windows
- ventana.py: la ventana es un marco Win32 propio con WebView2 dentro, manejado por COM con ctypes. Fuera pywebview, pythonnet y clr_loader y sus binarios sin firmar. Identidad en la barra de tareas, una sola copia abierta por carpeta de datos, selector de carpetas de Windows, rutas completas al soltar, avance en la barra de tareas, memoria de posición, aviso al cerrar con trabajo en marcha, y nada de navegador (menú, F5, zoom, permisos). Si el motor se cae, se monta de nuevo solo. - arranque.py y empaquetar.py: el paquete ya no lleva binario propio. OrganizadorLabstream.exe es pythonw.exe (firmado por la PSF) con otro nombre y arranca el programa desde sitecustomize; abre con Smart App Control encendido. Fuera PyInstaller. Al empaquetar se comprueba la firma de cada binario con Windows y se arranca el motor del paquete. - instalar.py: accesos directos en Inicio y Escritorio con la identidad de la ventana, y la línea en Configuración › Aplicaciones. Se ofrece una vez, la primera que se abre un paquete en un equipo. - Dos copias sobre los mismos datos ya no se pisan: la segunda trae la ventana al frente (antes abría el navegador y daba por huérfanas las conversiones en marcha de la primera). Si el puerto 8765 está ocupado por otro programa, se toma uno libre. - Por consola, el paquete ya no consulta si hay versión nueva con las actualizaciones automáticas apagadas. - Pruebas: probar.py 17, prueba_funcional.py 26 de 26 y prueba_ventana.py (nueva), que abre la ventana de verdad: 17 comprobaciones desde el código y 8 arrancando el paquete por su .exe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
68
arranque.py
Archivo normal
68
arranque.py
Archivo normal
@@ -0,0 +1,68 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r"""
|
||||
El arranque del paquete. Dentro del paquete este archivo se llama runtime\sitecustomize.py, que es el
|
||||
módulo que Python importa él solo cada vez que arranca.
|
||||
|
||||
OrganizadorLabstream.exe es pythonw.exe —el intérprete de Python, firmado por la Python Software
|
||||
Foundation— con otro nombre. Por eso Windows lo deja abrir con Smart App Control encendido, que es
|
||||
justo lo que no pasaba con el .exe de PyInstaller: aquel era un binario propio, sin firma, y Windows lo
|
||||
bloqueaba. Aquí no hay ningún binario propio: el motor es el de Python y el programa es su código.
|
||||
|
||||
Un intérprete al que no se le pasa nada no hace nada; este módulo es quien le dice qué hacer:
|
||||
|
||||
sin argumentos (doble clic) abre OrganiLAB
|
||||
con una carpeta o un comando el programa por consola (asistente, stock, instalar…)
|
||||
con un guion .py, -c o -m intérprete normal: lo usan el puente de Resolve y las pruebas
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def _avisar(base, traza):
|
||||
"""Sin consola, un fallo al arrancar sería una ventana que no aparece y nada más: se deja escrito y se dice."""
|
||||
ruta = os.path.join(base, "datos", "error-de-arranque.txt")
|
||||
try:
|
||||
os.makedirs(os.path.dirname(ruta), exist_ok=True)
|
||||
with open(ruta, "w", encoding="utf-8") as fh:
|
||||
fh.write(traza)
|
||||
except OSError:
|
||||
ruta = ""
|
||||
try:
|
||||
import ctypes
|
||||
ctypes.windll.user32.MessageBoxW(None, "OrganiLAB no pudo arrancar.\n\n" + traza.strip().splitlines()[-1][:300]
|
||||
+ (f"\n\nEl detalle quedó en:\n{ruta}" if ruta else ""), "OrganiLAB", 0x30)
|
||||
except Exception: # noqa
|
||||
pass
|
||||
|
||||
|
||||
def _arrancar():
|
||||
a0 = sys.argv[0] if sys.argv else ""
|
||||
if a0 in ("-c", "-m") or (a0.lower().endswith((".py", ".pyw")) and os.path.isfile(a0)):
|
||||
return # lo están usando de intérprete
|
||||
base = os.path.dirname(os.path.abspath(sys.executable))
|
||||
if not os.path.isfile(os.path.join(base, "organizador.py")):
|
||||
return # este motor no está dentro de un paquete de OrganiLAB
|
||||
sys.argv = [sys.executable] + [a for a in sys.argv if a]
|
||||
codigo = 0
|
||||
try:
|
||||
import organizador
|
||||
organizador.main()
|
||||
except SystemExit as ex:
|
||||
codigo = ex.code if isinstance(ex.code, int) else (0 if ex.code is None else 1)
|
||||
except KeyboardInterrupt:
|
||||
codigo = 130
|
||||
except BaseException: # noqa
|
||||
import traceback
|
||||
codigo = 1
|
||||
_avisar(base, traceback.format_exc())
|
||||
for salida in (sys.stdout, sys.stderr):
|
||||
try:
|
||||
salida.flush()
|
||||
except Exception: # noqa
|
||||
pass
|
||||
# Python sigue arrancando por debajo de este módulo: si se volviera, pasaría a su modo interactivo.
|
||||
# Se sale aquí, sin más ceremonia; para entonces el programa ya cerró lo que tenía abierto.
|
||||
os._exit(codigo)
|
||||
|
||||
|
||||
_arrancar()
|
||||
Referencia en una nueva incidencia
Block a user