Fenêtre superposant une autre fenêtre

Phidippides Messages postés 13 Statut Membre -  
Phidippides Messages postés 13 Statut Membre -
Bonjour,

Tout d'abord je n'ai pas beaucoup d'expérience avec Python, cela fait seulement quelques mois que je l'ai découvert.
Je suis en train de développer un solveur de sudoku mais j'ai un problème que je n'arrive pas à résoudre.
J'ai créé une première fenêtre pour ma grille de Sudoku qui est divisée en cellules.
Parfois évidemment, il peut y avoir plusieurs possibilités pour une même cellule jusqu'à ce que l'on déduise le seul chiffre possible.
Pour cela, j'ai créé une petite fenêtre aux dimensions de la cellule qui est elle-même divisée en 9 mini-cellules et dans laquelle l'utilisateur peut entrer les chiffres possibles.
Tout cela marche très bien mais si je clique sur le '-' (en haut à droite) de la fenêtre principale, celle-ci est iconifiée mais pas la ou les petites mini-grilles superposées aux cellules de mon Sudoku (ma fenêtre principale). De la même façon, si je superpose une autre fenêtre (par exemple mon moteur de recherche) à ma grille de Sudoku, cette dernière est effectivement cachée mais pas les petites fenêtres (mini-grilles) qui apparaissent toujours.
J'ai essayé de détecter un 'event' qui est 'Unmap' pour résoudre le problème. Cela marchait effectivement très bien quand j'étais sous Windows 7 mais maintenant que je tourne sous Linux (Ubuntu avec GNOME shell), cela ne marche plus. J'ai essayé des tas d'autres 'events' mais je n'arrive pas à m'en sortir.

Mes questions sont :

1) Quel est l'événement déclenché quand je clique gauche sur le '-' (en haut à droite) de la fenêtre principale, pour iconifier celle-ci ? Et comment récupérer cet événement pour lancer une fonction qui cacherait mes mini-grilles ?
2) Quand je recouvre ma grille de Sudoku (fenêtre principale) par une autre fenêtre (extérieure à mon logiciel), comment faire en sorte que mes mini-grilles soient aussi recouvertes ?

Je serais vraiment reconnaissant à ceux qui pourraient me donner un coup de pouce.

1 réponse

  1. Yoan Messages postés 11905 Statut Modérateur 2 356
     
    Après une recherche sur le terme Unmap, j'en déduis que vous utilisez Tkinter, ce qui est primordial à préciser.

    Si vous lisez l'anglais et cette discussion, il semble que le terme "unmap" n'a pas le même sens sous Linux, et en particulier ce n'est pas l'événement qui ce déclenche en cliquant sur le "-".

    Sur la même discussion, une solution avec wmctrl et xprop est proposée, mais vous allez vous retrouver avec du code non portable (différent selon s'il est sous Linux ou Windows).

    Pour déterminer si un autre événement pourrait vous intéresser, je vous suggère de créer un petit programme tout simple qui imprime tous les événements reçus 1 fois par seconde. Vous cliquez sur - et vous regarder ce qui a été affiché à l'écran.

    Je n'ai pas bien compris comment était organisée votre interface graphique et vos mini grilles. Elles sont de quelles classes vos mini grilles ? Classe Tk ?
    0
    1. Phidippides Messages postés 13 Statut Membre
       
      Bonjour Yoan

      Je n'ai pas pu détecter l'"event" quand on clique sur le '-' pour iconifier la fenêtre principale.
      J'ai en fait essayer d'afficher les "events" quand on clique gauche au niveau de la fenêtre Tk mais rien n'est affiché quand on clique sur le '-'. Par contre il m'affiche bien tous les "events" quand je clique n'importe où dans la fenêtre. Je vous donne un code de test ci-dessous :

      import tkinter as tk
      from tkinter.font import Font
      from collections import namedtuple
      
      RC = namedtuple('RC', 'rows columns')
      BBox = namedtuple('BBox', 'x y width height')
      
      
      class HintWindow(tk.Toplevel):        # fenêtre utilisée pour afficher les mini-grilles
      
          def __init__(self, bbox, bg='yellow', alpha=0.3, state='normal'):
              super().__init__(bg=bg)
              if state == 'withdrawn':
                  self.withdraw()
              self.overrideredirect(True)
              self.geometry("%dx%d+%d+%d" % (bbox.width, bbox.height, bbox.x, bbox.y))
              self.wm_attributes('-alpha', alpha)
      
      class Cell:        # Tout ce qui concerne les 81 (= 9 x 9) cellules de la grille de Sudoku
          _iid = None
          _square_cfg = dict(fill='white', width=1, outline='white',
                             activeoutline='red', activewidth=3, tag='cell')
          _hintcell = None
      
          @property
          def iid(self):
              return self._square
      
          def __init__(self, canvas, rc, coords, text_color='black'):
              self.canvas = canvas
              self.rc = rc
              self.coords = coords
              self.center = (coords[0] + coords[2]) // 2, (coords[1] + coords[3]) // 2
              self._square = canvas.create_rectangle( coords, self._square_cfg)
              self._label = canvas.create_text(self.center, fill=text_color, text='', font=canvas.font, tag='cell')
              self.value = 0
      
          def set_value(self, value):
              itemconfig = self.canvas.itemconfigure
              s = str(value) if value else ''
              itemconfig(self._label, text=s)
              self.value = value
      
          def create_hintcell(self, values):     # crée la fenêtre et la mini-grille incluse dans celle-ci
              '''hint as reusable toplevel'''
              w = None
              if self._hintcell is None:
                  bbox = self.canvas.root_bbox(self.iid, 1, 1)
                  w = HintWindow(bbox, bg='white', alpha=0.7)
                  hints = self._hintcell = HintsGrid(w, self)
                  hints.pack(fill='both', padx=2, pady=2)
              self._hintcell.load(values)
              return w
      
      class Grid(tk.Canvas):         # classe parent de la grille SudokuGrid et de la mini-grille HintsGrid qui effectue 
                                                          tout ce qui est commun aux 2 classes filles 
          _cfg = None
          cellsize = None
          font = None
          RC = None
          SPACING = None
          font_config = None
      
          def __init__(self, parent=None):
              self._cfg['width'] = self.RC[0] * (self.cellsize + self.SPACING)
              self._cfg['height'] = self.RC[1] * (self.cellsize + self.SPACING)
              super().__init__(parent, self._cfg)
      
              self.font = Font(**self.font_config)
              self._cells = {} # maps (r, c) to cell
      
              for r in range(self.RC.rows):
                  y0 = r * (self.cellsize + self.SPACING)
                  y1 = y0 + self.cellsize
                  for c in range(self.RC.columns):
                      x0 = c * (self.cellsize + self.SPACING)
                      x1 = x0 + self.cellsize
                      rc = (r+1, c+1)
                      self._cells[rc] = self.create_cell(rc, (x0, y0, x1, y1))
      
          def cell(self, r, c):
              if (r, c) in self._cells:
                  return self._cells[(r, c)]
      
          def root_bbox(self, iids, dx=0, dy=0):
              if not isinstance(iids, list):
                  iids = (iids,)
              x0, y0, x1, y1 = self.bbox(*iids)
              w = x1 - x0
              h = y1 - y0
              x = self.winfo_rootx() + x0
              y = self.winfo_rooty() + y0
              return BBox(x+dx, y+dy, w-dx, h-dy)
      
      class SudokuGrid(Grid):     # ma grille de Sudoku
          _cfg = dict(bd=0, highlightthickness=False, background='grey')
          cellsize = 63
          RC = RC(9, 9)
          SPACING = 2
          font_config = dict(family='Helvetica', size=(cellsize // 3))
          _toplevel = None
          _model = None
      
          def __init__(self, parent):
      
              self.create_cell = lambda rc, coords: Cell(self, rc, coords)
              super().__init__(parent)
              self._hints = []
      
          def load(self, initial_values):
              for r, values in enumerate(initial_values, 1):
                  [ self.cell(r, c).set_value(v) for c, v in enumerate(values, 1) if v ]
      
          def update(self):
              toplevel = self.toplevel
              toplevel.bind('<Unmap>', self.on_minimize, '+')     # c'est ici que cela coince
              super().update()
      
          @property
          def toplevel(self):
              if self._toplevel is None:
                  self._toplevel = self.winfo_toplevel()
              return self._toplevel
      
          def on_minimize(self, event):
              for h in self._hints:
                  h.withdraw()
      
          def hint_cell(self, r, c, values):
              self._hints.append(self.cell(r, c).create_hintcell(values))
      
      class HintsGrid(Grid):     # ma mini-grille incluse dans ma fenêtre HintWindow
          _cfg = dict(bd=0, highlightthickness=False, background='white')
          cellsize = 17
          RC = RC(3, 3)
          SPACING = 4
          font_config = dict(family='Helvetica', size=11)
      
          def __init__(self, parent, cell, values=None):
              self.create_cell = lambda rc, coords: Cell(self, rc, coords, text_color='blue')
              self._cell = cell
              super().__init__(parent)
              if values:
                  self.load(values)
      
          def load(self, values):
              z = 1
              for r in range(self.RC.rows):
                  for c in range(self.RC.columns):
                      cell = self.cell(r+1, c+1)
                      if z in values:
                          cell.set_value(z)
                      else:
                          cell.set_value(0)
                      z += 1
      
      if __name__ == '__main__':
      
          G = [ (0, 2, 0,  5, 0, 1,  0, 9, 0),
                (8, 0, 0,  2, 0, 3,  0, 0, 6),
                (0, 3, 0,  0, 6, 0,  0, 7, 0),
      
                (0, 0, 1,  0, 0, 0,  6, 0, 0),
                (5, 4, 0,  0, 0, 0,  0, 1, 9),
                (0, 0, 2,  0, 0, 0,  7, 0, 0),
      
                (0, 9, 0,  0, 3, 0,  0, 8, 0),
                (2, 0, 0,  8, 0, 4,  0, 0, 7),
                (0, 1, 0,  9, 0, 7,  0, 6, 0),
            ]
      
          app = tk.Tk()
          app.resizable(False, False)
          app.tk_focusFollowsMouse()
          grid = SudokuGrid(app)
          grid.load(initial_values=G)
      
          grid.pack(fill='both')
          grid.update()
      
          grid.hint_cell( 1, 1, (1, 2, 5, 6))
      
          app.mainloop()
      0
    2. jokler
       
      Bonjour.

      Les fenêtres toplevel dont les bordures ont été enlevées ne peuvent plus être gérées via le gestionnaire de fenêtre du système d'exploitation.

      https://www.tcl.tk/man/tcl8.4/TkCmd/wm.htm#M37

      Il serait donc sans doute plus simple d'insérer ces toplevels dans le canvas avec create_window pour ne pas se compliquer la vie.
      0
      1. Phidippides Messages postés 13 Statut Membre > jokler
         
        Merci jokler

        Je vais tester pour voir si ça me satisfait.
        0
      2. Phidippides Messages postés 13 Statut Membre > jokler
         
        Voilà j'ai fini de modifier et de tester et c'est parfait.
        Encore grand merci à toi jokler.
        0