# imports
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk
import sqlite3
import re
from glade import UI
from systeme import ST
from download import Download
from config import Config
class Handler:
"""
classe pour gérer l'interface utilisateur
"""
def __init__(self, fichier_config, fichier_db):
# =============================================
"""
Initialise une nouvelle instance de la classe Handler
"""
# chemin vers les fichiers de config et de base de données
self.fichier_config = fichier_config
self.fichier_db = fichier_db
# on fixe les variables de pagination
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
self.pagination_lignes_par_page = 500
# on conserve l'ID de la base de données correspondant
# au jeu sélectionné pour pouvoir mettre a jour l'état du
# jeu si on demande le téléchargement
self.id_selection_telechargement = 0
# reference au processus de telechargement
self.download_process = None
# on conserve les variables de la sélection en cours,
# valeurs attribuées dans 'on_tree_selection_liste_jeux_changed()'
self.selection_jeux_model = 0
self.selection_jeux_treeiter = 0
self.selection_liste_jeux_changed_en_cours = ''
# dictionnaire des sections de jeux
self.dict_sections_jeux = {
'All games': 'all',
'Numbers': 'nombres',
'A': 'a','B': 'b','C': 'c','D': 'd','E': 'e','F': 'f','G': 'g','H': 'h','I': 'i','J': 'j',
'K': 'k','L': 'l','M': 'm','N': 'n','O': 'o','P': 'p','Q': 'q','R': 'r','S': 's','T': 't',
'U': 'u','V': 'v','W': 'w','X': 'x','Y': 'y','Z': 'z'
}
# on fixe la combo box avec les sections de jeu
for cle in self.dict_sections_jeux:
myiter = UI.list_store_combo_box_sections_jeux.insert_before(None, None)
UI.list_store_combo_box_sections_jeux.set_value(myiter, 0, cle)
UI.combo_box_sections_jeux.set_active(0)
# dictionnaire des doublons
self.selection_doublons = "all"
self.dict_doublons = {
'Full list': 'all',
'without duplicates': 'doublons off',
'show duplicates': 'doublons on'
}
# on fixe la combo box avec les doublons
for cle in self.dict_doublons:
myiter = UI.list_store_combo_box_doublons.insert_before(None, None)
UI.list_store_combo_box_doublons.set_value(myiter, 0, cle)
UI.combo_box_doublons.set_active(0)
# dictionnaire des etats de jeu
self.dict_etats_jeux = {
'All games': '',
'Downloaded': 'X',
'Not downloaded': '-',
'Download in progress': '=',
'Download error': '!'
}
# on fixe la combo box avec les etats de jeu
for cle in self.dict_etats_jeux:
myiter = UI.list_store_combo_box_etats_jeux.insert_before(None, None)
UI.list_store_combo_box_etats_jeux.set_value(myiter, 0, cle)
UI.combo_box_etats_jeux.set_active(0)
def update_tree_view_liste_jeux(self):
# ====================================
"""
Cette fonction est utilisée pour mettre à jour le 'tree view'
contenant la liste des jeux.
"""
# on ouvre la base de données
db_connect = sqlite3.connect(self.fichier_db)
db_connect.create_function('regexp', 2, lambda x, y: 1 if re.search(x,y) else 0)
db_cursor = db_connect.cursor()
# requête de base
requete = f"{ST.requete_base_systemes} "+\
f"{ST.requete_add_sections_jeux} "+\
f"{ST.requete_add_etats_jeux} "+\
f"{ST.requete_add_recherches} "+\
"ORDER BY nom ASC "
# listes
liste_complete = []
liste_sans_doublons = []
liste_doublons = []
# on effectue un trie de la liste
# avec ou sans doublons
ligne_precedente = ""
for row in db_cursor.execute(requete):
liste_complete.append(row)
if row[2] != ligne_precedente:
liste_sans_doublons.append(row)
else:
liste_doublons.append(row)
ligne_precedente = row[2]
# on choisit la liste à utilisé pour alimenter
# la 'list store' a allé chercher depuis 'on_combo_box_doublons_changed()'
liste_selectionnee = liste_complete
match self.selection_doublons:
case 'doublons off':
liste_selectionnee = liste_sans_doublons
case 'doublons on':
liste_selectionnee = liste_doublons
# on récupère le nombre de jeux par machine
# depuis la classe 'Systeme' 'ST.dict_count_from_machine[ST.cle_requete_jeux_en_cours]'
# le nombre de jeux de chaques machines, systèmes sont récuperés au moment
# de l'initialisation de la classe et sont stockés dans un dictionnaire avec comme
# clés les clés systèmes '[0], [0, 0], [1], [0,0]...'
nombre_de_jeux_par_machine = ST.dict_count_from_machine[ST.cle_requete_jeux_en_cours]
# on met en place la pagination et on met à jour dans l'interface
# le nombre de jeux dans les labels infos
nombre_total_ligne = len(liste_selectionnee)
UI.label_info_jeux.set_markup(f"<b>{nombre_total_ligne} / {nombre_de_jeux_par_machine} Games</b>")
self.pagination_nombre_page = int( nombre_total_ligne / self.pagination_lignes_par_page )
UI.label_info_page.set_markup(f"<b><big>{self.pagination_numero_page + 1} / {self.pagination_nombre_page + 1}</big></b>")
# on met en place la limite et l'offset
# pour gerer le multi pages
limit = self.pagination_lignes_par_page
offset = ( self.pagination_lignes_par_page * self.pagination_numero_page )
UI.list_store_tree_view_liste_jeux.clear() # on vide la 'list store'
# on insère dans la liste selectionnee
for i in range(limit):
if offset + i>= nombre_total_ligne: break
myiter = UI.list_store_tree_view_liste_jeux.insert_before(None, None)
UI.list_store_tree_view_liste_jeux.set_value(myiter, 0, liste_selectionnee[offset + i][0])
UI.list_store_tree_view_liste_jeux.set_value(myiter, 1, (liste_selectionnee[offset + i][1] if liste_selectionnee[offset + i][1] != "-" else ""))
UI.list_store_tree_view_liste_jeux.set_value(myiter, 2, liste_selectionnee[offset + i][2])
UI.list_store_tree_view_liste_jeux.set_value(myiter, 3, liste_selectionnee[offset + i][3])
# on met à jour la sélection de la nouvelle liste
# et on ferme la base de données
self.selection_liste_jeux_changed_en_cours = ''
UI.tree_view_liste_jeux.set_cursor(0)
db_connect.close()
def on_window_scrom_destroy(self, *args):
# =======================================
"""
fermer la fenêtre
"""
Gtk.main_quit()
def on_button_quitter_clicked(self, button):
# ==========================================
"""
bouton 'quitter'
"""
Gtk.main_quit()
def on_tree_selection_liste_jeux_changed(self, selection):
# ========================================================
"""
selection d'un jeu dans la liste, on recherche le lien
de telechargement du jeu dans la base de données et on
on l'affiche dans le widget 'UI.entry_lien_telechargement'
la récupération du lien s'éffectue avec l'ID de l'entrée,
colonne ( cachée ), dans base de données 'db_cursor.fetchone()[0]'
"""
# on conserve les indices de la selection
self.selection_jeux_model, self.selection_jeux_treeiter = selection.get_selected()
# on teste la valite de la selection
if self.selection_jeux_treeiter is not None:
# on récupère l'indice de la selection
treepath = self.selection_jeux_model.get_path(self.selection_jeux_treeiter)
# si l'indice est différent de la sélection en cours
# on se connecte à la base de données pour récupérer le lien
# de téléchargement du jeu et on affiche le lien dans
# 'UI.entry_lien_telechargement' """
if treepath.get_indices() != self.selection_liste_jeux_changed_en_cours:
# on se connecte à la base de données
db_connect = sqlite3.connect(self.fichier_db)
db_cursor = db_connect.cursor()
# on récupère l'ID du jeu depuis la colonne cachée du treeview
# '[self.selection_jeux_treeiter][0]' correspond à la colonne 'ID' """
self.id_selection_telechargement = self.selection_jeux_model[self.selection_jeux_treeiter][0]
# on compose la requête et on l'exécute
requete = f"SELECT lien FROM jeux WHERE id='{self.id_selection_telechargement}'"
db_cursor.execute(requete)
db_connect.commit()
# on insère le lien de téléchargement du jeu dans
# 'UI.entry_lien_telechargement'
UI.entry_lien_telechargement.set_text(db_cursor.fetchone()[0])
# on conserve l'indice
self.selection_liste_jeux_changed_en_cours = treepath.get_indices()
# on ferme la base
db_connect.close()
def on_tree_selection_systemes_changed(self, selection):
# ======================================================
"""
selection d'un systeme dans la liste
"""
# récupérer la clé requête correspondant à la sélection
model, treeiter = selection.get_selected()
treepath = model.get_path(treeiter)
cle_requete_jeux = str(treepath.get_indices())
# on conserve la cle requete en cours
# pour l'affichage du nombre total de jeux disponibles
# dans 'update_tree_view_liste_jeux()'
ST.cle_requete_jeux_en_cours = cle_requete_jeux
# si le clé est supèrieure à 4 il s'agit de la sélection
# d'un téléverseur, on rend visible la colonne 'téléverseur'
if len(cle_requete_jeux) > 4:
UI.tree_view_column_televerseurs.set_visible(False)
else:
UI.tree_view_column_televerseurs.set_visible(True)
# on conserve la requete systeme de base en cours
ST.requete_base_systemes = ST.dict_requetes[cle_requete_jeux]
# afficher le nom du systeme dans 'UI.label_info_machine' et dans
# 'UI.entry_repertoire_telechargement_systeme'
machine = (ST.requete_base_systemes.split('C.machine = ')[1].split(' ')[0]).replace("'", "")
UI.label_info_machine.set_text(f"{machine}")
# afficher le nom de la machine si 'check_button_ajouter_repertoire_systeme' est 'True'
if UI.check_button_ajouter_repertoire_machine.get_active():
UI.entry_repertoire_telechargement_systeme.set_text(f"/{machine}")
# mettre à jour le liste des jeux a 0 pour la nouvelle liste
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
self.update_tree_view_liste_jeux()
def on_entry_recherches_activate(self, entry):
# ============================================
"""
on effectue une action de recherche avec la valeur de 'entry'
dans la liste de jeux en cours et on met a jour la liste
"""
ST.requete_add_recherches = ""
search_text = entry.get_text()
if search_text != "":
ST.requete_add_recherches = f"AND A.nom LIKE '%{search_text}%'"
# on remet la pagination a 0
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
# on met à jour la liste
self.update_tree_view_liste_jeux()
def on_entry_recherches_icon_release(self, start_pos, end_pos, user_data):
# ========================================================================
"""
action sur l'icone pour effacer la recherche en cours
"""
ST.requete_add_recherches = ""
# on remet la pagination a 0
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
# on met à jour la liste
self.update_tree_view_liste_jeux()
def on_combo_box_sections_jeux_changed(self, combo):
# ==================================================
"""
action sur la selection d'une nouvelle section qui
correspond a : liste complete, number, A, B, C, etc
"""
# on recupere la section choisie depuis le combobox
tree_iter = combo.get_active_iter()
model = combo.get_model()
combo_value = model[tree_iter][0]
section = self.dict_sections_jeux[combo_value]
# on met à jour la requête
ST.requete_add_sections_jeux = f"AND A.nom regexp '^(?i:{section})'"
match section:
case 'all':
ST.requete_add_sections_jeux = ""
case 'nombres':
ST.requete_add_sections_jeux = "AND A.nom regexp \"^[0-9-']\""
# on remet la pagination a 0
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
# on met à jour la liste
self.update_tree_view_liste_jeux()
def on_combo_box_etats_jeux_changed(self, combo):
# ===============================================
"""
action sur la selection d'un nouvel etat des jeux qui
correspond a : Telecharge, Non Telecharge, etc
"""
# on recupere un des etats depuis le combobox
tree_iter = combo.get_active_iter()
model = combo.get_model()
combo_value = model[tree_iter][0]
# on met à jour la requête
symbole_etat = self.dict_etats_jeux[combo_value]
if symbole_etat == "": ST.requete_add_etats_jeux = ""
else: ST.requete_add_etats_jeux =f"AND A.telechargement = '{symbole_etat}'"
# on remet la pagination a 0
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
# on met à jour la liste
self.update_tree_view_liste_jeux()
def on_combo_box_doublons_changed(self, combo):
# =============================================
"""
action sur la selection d'un affichage de la liste
avec ou sans doublons
"""
# on recupere un des etats depuis le combobox
tree_iter = combo.get_active_iter()
model = combo.get_model()
combo_value = model[tree_iter][0]
# on met à jour la requête
self.selection_doublons = self.dict_doublons[combo_value]
# on remet la pagination a 0
self.pagination_numero_page = 0
self.pagination_nombre_page = 0
# on met à jour la liste
self.update_tree_view_liste_jeux()
def on_button_page_suivante_clicked(self, button):
# ================================================
"""
action sur le bouton 'page suivante'
si le numero de page est correct on
passe a la page suivante et on met a jour la liste des jeux
"""
if self.pagination_numero_page < self.pagination_nombre_page:
self.pagination_numero_page += 1
self.update_tree_view_liste_jeux()
def on_button_page_precedente_clicked(self, button):
# ==================================================
"""
action sur le bouton 'page precedente'
si le numero de page est correct on
passe a la page precedente et on met a jour la liste des jeux
"""
if self.pagination_numero_page > 0:
self.pagination_numero_page -= 1
self.update_tree_view_liste_jeux()
def on_button_telecharger_clicked(self, button):
# ==============================================
"""
action sur le bouton 'telecharger'
"""
# on conserve la reference au processus de telechargement
# pour pouvoir l'arreter
self.download_process = Download(self.id_selection_telechargement, self.fichier_db)
def on_button_arreter_telechargement_clicked(self, button):
# =========================================================
"""
action sur le bouton 'arreter telechargement'
"""
# arret du processus de telechargement
self.download_process.terminate()
def on_check_button_ajouter_repertoire_machine_toggled(self, button):
# ===================================================================
"""
action sur le toggle button pour ajouter ou non
le repertoire de la machine lors du telechargement
"""
# on inverse l'etat du toggle button
# si c'est true on affiche le nom de la machine
# sinon on efface la zone
check_repertoire_systeme = False
if UI.check_button_ajouter_repertoire_machine.get_active():
check_repertoire_systeme = True
UI.entry_repertoire_telechargement_systeme.set_text(f"/{UI.label_info_machine.get_text()}")
else:
UI.entry_repertoire_telechargement_systeme.set_text('')
# on sauvegarde l'etat du toggle button
Config.save(self, 'repertoire_telechargement_systeme_etat', check_repertoire_systeme)
def on_entry_repertoire_telechargement_activate(self, entry):
# ===========================================================
"""
la zone de saisie du repertoire de telechargement est activee
"""
# on sauvegarde le nouveau chemin de telechargement
Config.save(self, 'repertoire_telechargement', f"{UI.entry_repertoire_telechargement.get_text()}")
# on deplace le focus de la zone de saisie du chemin de telechargement
# vers le toggle button
UI.check_button_ajouter_repertoire_machine.grab_focus()
def on_entry_commande_telechargement_activate(self, entry):
# =========================================================
"""
action sur le nombre de connexion, a la base entry_commande_telechargement
devait contenir une commande de telechargement style : axel -c -n 5
"""
# on sauvegarde le nouveau nombre de connexion
Config.save(self, 'nombre_connexion', f"{UI.entry_commande_telechargement.get_text()}")
# on deplace le focus de la zone de saisie du nombre de connexion
# vers le bouton 'telecharger'
UI.button_telecharger.grab_focus()
def on_tree_view_liste_jeux_button_press_event(self, tv, event):
# ==============================================================
"""
afficher un menu avec la souris, bouton de droite pour
modifier l'état d'une sélection de jeu, 'Téléchargé', 'Non téléchargé'
'Téléchargement en cours'
"""
# si il y a une action du bouton de droite de la souris
# et si un jeu est selectonne
if event.button == 3 and self.selection_liste_jeux_changed_en_cours != "":
# creation du menu
treeview_menu = Gtk.Menu()
# creation des items du menu
for key in self.dict_etats_jeux:
if self.dict_etats_jeux[key] != '':
menu_item = Gtk.MenuItem(key)
menu_item.connect("activate", self.pop_menu)
treeview_menu.append(menu_item)
else:
menu_item = Gtk.MenuItem("")
treeview_menu.append(menu_item)
menu_item = Gtk.MenuItem("")
# on ajoute le menu au treeview
treeview_menu.append(menu_item)
# on affiche le menu
treeview_menu.popup(None, None, None, None, 1, 0)
treeview_menu.show_all()
def pop_menu(self, menu):
# =======================
"""
creation de la fenetre du menu avec les differents etats
"""
# si le label du menu n'est pas '-'
# on met a jour l'état de téléchargement dans la base de données
if self.dict_etats_jeux[menu.get_label()] != '-':
self.selection_jeux_model[self.selection_jeux_treeiter][1] =\
self.dict_etats_jeux[menu.get_label()]
else:
self.selection_jeux_model[self.selection_jeux_treeiter][1] = " "
# on récupère l'identifiant et l'état du jeu
id = self.selection_jeux_model[self.selection_jeux_treeiter][0]
etat = self.dict_etats_jeux[menu.get_label()]
# on ouvre la base de données
db_connect = sqlite3.connect(self.fichier_db)
db_cursor = db_connect.cursor()
# on met a jour l'état du jeu dans la base de données
db_cursor.execute('''UPDATE jeux SET telechargement = ? WHERE id = ?''', (etat, id))
db_connect.commit()
db_connect.close()