Le poste de contrôle (PC) / Les paramètres /
Paramètres : 2 - Persistance

Paramètres enregistrés dans un fichier pickle


Avant-propos


Description

Objectif:



Conventions :


Exemple traité, la vue :


Le déplacement :

Créez le fichier /pc/pkl.py, copiez-y le code suivant :

from functions.utils import Dictionary
import pickle
import os


class Pkl:
    def __init__(self, o_scene):
        self.o_scene = o_scene
        self.fic_pkl = None
        self.od_pkl = None
        self.restore()

    def restore(self):
        """ Passage unique, à l'instantiation.
        - Tentative de lecture du fichier params.pkl, produisant le dictionnaire de paramètres.
        - Si échec (inexistant ou non conforme), on utilise un dictionnaire vide.
        - Transformation en super-dictionnaire (self.od_pkl), grace à la classe Dictionary.
        """
        self.fic_pkl = f'{os.getcwd()[:-2]}backups{os.sep}{self.o_scene.graph_name}{os.sep}params.pkl'
        try:
            """ Lecture du fichier pkl. """
            with open(self.fic_pkl, 'rb') as pk:    # Pas d'encoding pour les fichiers binaires.
                d_pkl = pickle.load(pk)
        except (Exception,):
            d_pkl = {}
        self.od_pkl = Dictionary(d_pkl)        # OrderedDict de tout le graphe (vue + nodes + edges + groupes + labels)

    def backup(self):
        """ Méthode appelée chaque fois qu'une modification intervient dans le graphe. """
        try:
            with open(self.fic_pkl, 'wb') as pk:  # 1) Ouveture ET écrasement du fichier pkl.
                pickle.dump(self.od_pkl, pk, pickle.HIGHEST_PROTOCOL)  # 2) Écriture dans un fichier vierge.
        except (Exception,):
            return False
        return True


Dans le fichier ctrl_scene.py, déclarez le super-dictionnaire unique :

  1. Import de la classe Pkl que nous venons de créer. Dans les imports :
    ctrl_scene.ui > imports
    from pkl import Pkl
  2. Déclaration de l'attribut self.o_pkl. Dans ctrl_scene.py > CtrlScene.__init__(), avant self.setup() :
            self.o_pkl = None           # Instance de Pkl (Une seule instance par graphe)
    
  3. Instanciation de la classe Pkl (code ci-dessus) dans l'objet self.o_pkl (ligne ci-dessus). Dans la méthode self.setup(), au début :
    ctrl_scene.py > CtrlScene.setup()
            """ Paramètres. """
            self.o_pkl = Pkl(self)
    

Dans le fichier ui_view.py,

    def _get_position(self):
        """ Centre de la vue par rapport au centre de la scène => Négatifs si le centre-scène est en bas à droite. """
        p_centre = self.width() // 2, self.height() // 2        # p_ = point
        p_scene = self.mapToScene(*p_centre)                # Convertisseur => Changement de coordonnées.
        return round(p_scene.x()), round(p_scene.y())

    def right_button_pressed(self, ev):
        fake_event = QMouseEvent(ev.type(), ev.localPos(), ev.screenPos(),
                                 Qt.LeftButton, ev.buttons() | Qt.LeftButton, ev.modifiers())
        self.setDragMode(QGraphicsView.ScrollHandDrag)  # Icone-Curseur souris => main fermée.
        super().mousePressEvent(fake_event)             # Après setDragMode() -> Obligatoire !

    def right_button_released(self, ev):
        fake_event = QMouseEvent(ev.type(), ev.localPos(), ev.screenPos(),
                                 Qt.LeftButton, ev.buttons() | Qt.LeftButton, ev.modifiers())
        super().mouseReleaseEvent(fake_event)           # Avant setDragMode() -> Obligatoire !
        self.setDragMode(QGraphicsView.RubberBandDrag)  # Curseur = pointeur par défaut.

        """ Enregistrement. """
        self.od_pkl.write(['UiView', 'position'], self._get_position())
        self.o_scene.o_pkl.backup()

    def mousePressEvent(self, ev):
        if ev.button() in [Qt.RightButton, Qt.MiddleButton]:
            """ Molette ou bouton droit appuyé. """
            self.right_button_pressed(ev)
        super().mousePressEvent(ev)

    def mouseReleaseEvent(self, ev):
        if ev.button() in [Qt.RightButton, Qt.MiddleButton]:
            """ Molette ou bouton droit relâché. """
            self.right_button_released(ev)
        super().mouseReleaseEvent(ev)

Bien sûr, ne pas oublier d'importer les classes nécessaires. Ici QMouseEvent :
ui_view.py > imports

from PyQt5.QtGui import QMouseEvent
Le zoom :

Dans le fichier ui_view.py :

        """ Zoom. """
        self.zoom = 0
        self.zoom_range = -5, 5
        self.zoom_factor = 1.25
        self.zoom_step = 1
    def wheelEvent(self, ev):
        angle = ev.angleDelta()
        if angle.x() != 0:
            """ Inhibition des 2 switches latéraux dans la molette de la souris. """
            return

        if angle.y() > 0 and self.zoom < self.zoom_range[1]:        # Limite.
            """ Molette roule vers le haut. """
            zoom_factor = self.zoom_factor
            self.zoom += self.zoom_step
        elif angle.y() < 0 and self.zoom > self.zoom_range[0]:      # Limite.
            """ Molette roule vers le bas. """
            zoom_factor = 1 / self.zoom_factor
            self.zoom -= self.zoom_step
        else:
            """ Hors limites. """
            return

        """ Zoom dans les limites. """
        self.scale(zoom_factor, zoom_factor)

        """ Enregistrement. """
        self.od_pkl.write(['UiView', 'zoom'], self.zoom)
        """ Enregistrement de la position du centre de la vue nécessaire, car le zoom l'a modifié. """
        self.od_pkl.write(['UiView', 'position'], self._get_position())
        self.dt.delay(self.o_scene.o_pkl.backup, 500)   # Évite la répétition excessive (mitraillage).
        self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
        """ Zoom. """
        self.zoom = self.od_pkl.read(['UiView', 'zoom'], 0)
        """ La raison étant géométrique, il faut procéder à une élévation à la puissance. """
        zoom_factor = self.zoom_factor**self.zoom   # Rappel x^0 = 1
        self.scale(zoom_factor, zoom_factor)

Bonjour les codeurs !