Como todo proyecto que uno empieza, hay una situación que nos lleva al punto de empezarlo. Ahora mismo ya no puedo explotar mi lado gamer porque suicide a mi portátil (si el XPS...). Pero hace unos meses estuve con los chicos de JugandoEnLinux (JeL de ahora en adelante) a full con juegos como Assetto Corsa, rFactor 2, Automibilista 2, entre otros. Lo malo de estos juegos es que no tienen soporte nativo para linux, durante mucho tiempo estuvimos peleando con WineHQ pero hace un par de años Steam lanzó Proton/SteamPlay una funcionalidad basada en WineHQ que permite ejecutar juegos nativos de windows en linux. A río pasado y con la SteamDeck en la mano sabemos por qué. Esta incursión de Steam dio un empuje enorme en la compatibilidad de juegos que pueden ejecutarse en linux, trayendo gran variedad y jugadores a linux. Pueden consultar la base de datos en ProtonDB.
Esto nos dejo disfrutar de los juegos que les nombre arriba y pueden pensar que "ya esta, teniendo esto que más se puede pedir?". Bueno, la comunidad también desarrolla utilidades que explotan diferentes aspectos o características que los mismos no tienen. Por nombrar 2 ejemplos:
- CrewChief: Con esta aplicación tenemos un jefe de equipo que nos va hablando avisándonos de todo lo que sucede a nuestro alrededor.
- SimHub: Nos crea una capa sobre el juego en el que podemos visualizar tableros personalizados, radares, mapas información... etc.
- Content Manager: como su nombre lo indica, un manejador de contenido que se puede agregar al Assetto Corsa.
E incluso los mismos juegos muchas veces traen sus propias utilidades, y es donde Proton/SteamPlay renguea un poco, este detecta el ejecutable del juego y solo nos deja lanzar ese ejecutable, dejándonos muy complicado poder abrir dentro del mismo entorno de emulación múltiples aplicaciones y utilidades. Esto fue lo que me llevo a pensar esta aplicación.
Pensando la lógica
Como dijimos antes, Proton te deja lanzar solo una aplicación y nos deja de manera mas o menos fácil elegir cual ejecutar, así que podemos crear un lanzador que reemplace el ejecutable original y podamos abrir el juego y otras aplicaciones dentro del mismo entorno a la vez que también poder agregarle argumentos, tiene que ser un ejecutable de windows (un archivo .EXE) y no puede ser una aplicación de terminal. Quería desde hace un tiempo hacer una aplicación gráfica en python, tenia mas o menos las bases de Tkinter así que esta era mi oportunidad. Hice un pequeño bosquejo de como quería que luciera y me puse manos a la obra.
Tenia pensado tener una sola configuración para todos los juegos, así que pensé en crear una pestaña para cada uno, en la imagen vemos que hay un cuadro de texto para poner el nombre del juego, y el botón para crearlo. Una vez creada la pestaña, se crea y se activa un botón para eliminarla y una caja de texto para agregar los ejecutables que se agregan para ser lanzados, si no hay una ruta especificada en la caja utiliza, se abre el cuadro de dialogo que nos provee Tkinter, nos agrega el botón del ejecutable y el cuadro de texto para los argumentos junto al botón de eliminar la aplicación. Ademas, tendremos el botón de guardar configuración.
Manos a la obra
Crear una aplicación gráfica con Tkinter es bastante fácil con un pequeño script lo sacas y es más o menos lo que hice al principio.
ProtonLauncherGui(ttk.Frame) |
---|
__init__(parent=None) |
create_widgets() |
arrange() |
add_notebook_tab(name=None) |
forget_tab() |
add_app(app_name=None, frame=None, args=None) |
open_app() |
delete_app() |
save_config() |
load_config() |
exist_tab(name) |
get_args(app) |
get_tab_by_text(text=None) |
Los widgets estáticos eran los fáciles, para no repetir el nombre de las pestañas las consulto a la lista de frames hijos que lleva el widget del Notebook. Además, eliminar la pestaña seleccionada era fácil ya que se puede saber qué frame tiene el foco al momento de presionar el botón. Los ejecutables eran agregados en un grid (o cuadricula) y encontrar la manera de identificarlos para poder eliminarlos me costó bastante, tuve que escarbar bastante dentro de tkinter hasta que descubrí que es el widget padre el que conoce la ubicación de los elementos hijos y no los elementos mismos o el grid.
La Beta 0.4
Como podrán ver, más o menos la aplicación funcionaba, por un lado sabia que no era lo mejor y por el otro @leillo1975 me dice, mira a ver si puedes agregar el icono de aplicación en lugar de la ruta completa. Eso era un reto interesante, no sabia si iba a poder extraer el icono de los ejecutables, pero me puse manos a la obra, con lo que seria la próxima beta. Lo que me ayudo a extraer los iconos fue una librería llamada icoextract es fácil ligera y rápida, más no se puede pedir. Después de practicar un poco me fui a hacer un poco de re factorizar, cambiar los nombres de las variables y crear algunas clases más para solventar el problema de eliminar los ejecutables.
ProtonLauncherGui(ttk.Frame) | Notebook.(ttk.Notebook) | Game.(ttk.Frame) | App(ttk.Frame) |
---|---|---|---|
create_widgets() | add(game, text) | create_widgets() | create_widgets() |
arrange() | settings() | arrange() | arrange() |
add_game_tab(name) | exist() | add_app() | open_app() |
forget_tab() | delete_app(app) | get_icon() | |
save_config() | settings() | delete_app() | |
load_config() | load_config() | settings() | |
load_config() |
Fue un cambio importante, separe el notebook, el juego y las aplicaciones en clases, me ayudo a simplificar el guardar y cargar la configuración, eliminar la fila con las aplicaciones y no tener que escarbar tanto dentro de Tkinter para lograrlo. El frame principal carga solo la configuración correspondiente a las pestañas del notebook, y delega a la clase game, cargar la configuración relativa a las aplicaciones. Para guardar la configuración funciona igual. Ya no necesito saber en que fila esta la aplicación que se quiere eliminar, ahora le pide al frame Game que lo elimine de la lista y luego se destruye asimismo y a todos los widgets que contiene.
Pasándonos al lado oscuro de la fuerza
Por ultimo lo que me quedaba era convertirlo en un ejecutable de windows, en realidad este no es el paso final, ya lo había utilizado en la beta 0.3. Para eso use la librería auto-py-to-exe, la librería se abre en el navegador y nos da muchas opciones, la verdad es que no me puse a toquetear mucho, solo lo necesario para funcione con Proton. En realidad lo que hace es empaquetar el archivo .py y el entorno de python en un archivo .exe.
Inconvenientes, bueno unos cuantos, para empezar no funciona desde linux, así que tuve que instalar una maquina virtual con windows, instalar git, python, y auto-py-to-exe, clonar el repositorio e instalar las dependencias del repositorio, porque este fue el segundo problema que me encontré, si las dependencias no están instaladas no las agrega automáticamente. Una vez elegido el icono y activada la opción de depuración se empaqueta y lo probamos en Proton.
Finalmente
Finalmente funciona, voy a dejar pendiente actualizar este articulo con un video con la demo, porque por un lado me quedo un articulo bestialmente largo y por el otro todavía no tengo mi súper pc gamer con "ledes", ja. Pero siéntanse libres de probar la aplicación cuando quieran y dejarme los comentarios por los medios de contacto, que lo disfruten y nos estamos leyendo.