Outils pour utilisateurs

Outils du site


issue53:tutopython

Queue Up, Queue Up If you've ever waited in line to buy a movie ticket, you've been in a queue. If you've ever had to wait in traffic at rush hour, you've been in a queue. If you've ever waited in a government office with one of those little tickets that says you’re number 98, and the sign says “Now serving number 42,” you've been in a queue. In the world of computers, queues are common. As a user, most times, you don't have to think about them. They are invisible to the user. But if you ever have to deal with realtime events, you will eventually have to deal with them. It's just data of one type or another, waiting in line for its turn to be processed. Once it's in the queue, it's there until it gets accessed, and then it's gone. You can't get the value of the next data item unless you pull it out of the queue. You can't, for example, get the value of the 15th item in the queue. You have to access the other 14 items first. Once it's accessed, it's out of the queue. It's gone, and unless you save it to a long-term variable, there's no way to get the data back.

En file, en file !

Si vous avez déjà fait la queue pour acheter un billet de cinéma, vous avez été dans une file d'attente. Si vous avez jamais eu à attendre dans les bouchons aux heures de pointe, vous avez été dans une file d'attente. Si vous avez déjà attendu dans un bureau administratif avec l'un de ces petits billets qui dit que vous êtes le numéro 98 et le panneau qui affiche « Numéro actuel : 42 », vous avez été dans une file d'attente.

Dans le monde des ordinateurs, les files d'attente sont très répandues. En tant qu'utilisateur, la plupart du temps vous n'avez pas à vous en préoccuper. Elles sont invisibles pour l'utilisateur. Mais si jamais vous avez à faire face à des événements en temps réel, vous allez finir par avoir à traiter avec elles. Il s'agit simplement de données d'un type ou d'un autre, qui attendent dans la file leur tour d'être traitées. Une fois qu'elles sont dans la file, elles attendent jusqu'à être traitées puis disparaissent. Vous ne pouvez pas connaître la valeur de l'élément de donnée suivant, sauf si vous le sortez de la file d'attente. Vous ne pouvez pas, par exemple, obtenir la valeur du quinzième élément de la file d'attente : il vous faut d'abord accéder aux 14 autres éléments. Une fois qu'un élément est consulté, il sort de la file d'attente. Il a disparu et il n'y a aucun moyen de récupérer les données à moins de les enregistrer dans une variable à long terme.

There are multiple types of queues. The most common ones are FIFO (First In, First Out), LIFO (Last In, First Out), Priority, and Ring. We'll talk about ring queues another time. FIFO queues are what we see in everyday life. All of the examples I listed above are FIFO queues. The first person in the line gets handled first, moves on, then everyone moves up one spot in the line. In a FIFO buffer, there is (within reason) no limit to the number of items it can hold. They just stack up in order. As an item is handled, it is pulled out (or dequeued) of the queue, and everything moves closer to the front of the queue by one position. LIFO Queues are less common in life, but there are still real-world examples. The one that comes to mind most quickly is a stack of dishes in your kitchen cabinet. When the dishes are washed and dryed, they get stacked in the cabinet. The last one in on the stack is the first one that comes out to be used. All the rest have to wait, maybe for days, to be used. It's a good thing that the movie ticket queue is FIFO, isn't it? Like the FIFO queue, within reason, there is no limit to the size of a LIFO queue. The first item in the queue has to wait as newer items are pulled out of the buffer (plates pulled off the stack) until it's the only one left.

Il existe plusieurs types de files d'attente. Les plus courantes sont FIFO (« First In, First Out » ou premier entré, premier sorti), LIFO (« Last In, First Out » ou dernier entré, premier sorti), priorité et anneau. Nous parlerons des files d'attente anneau une autre fois.

Les files d'attente FIFO sont celles que nous voyons dans la vie quotidienne. Tous les exemples que j'ai énumérés ci-dessus sont des files d'attente FIFO. La première personne dans la ligne est traitée d'abord, s'en va, puis tout le monde se déplace d'une place dans la ligne. Dans un tampon FIFO, il n'y a pas de limite (sauf celle de la raison) au nombre d'éléments qu'il peut contenir. Ils s'empilent simplement dans l'ordre. Lorsqu'un élément est traité, il est sorti de la file, et tous les autres se rapprochent d'une position du début de la file d'attente.

Les files d'attente LIFO sont moins fréquentes dans la vie, mais il existe encore des exemples réels. Celui qui vient tout de suite à l'esprit est l'exemple d'une pile d'assiettes dans votre placard de cuisine. Lorsque les assiettes sont lavées et séchées, elles s'empilent dans le placard. La dernière arrivée sur la pile est la première qui sera réutilisée. Tout le reste attend, peut-être pendant des jours, pour être utilisé. C'est une bonne chose que la file d'attente pour un billet de cinéma soit FIFO, n'est-ce pas ? Comme pour la file d'attente FIFO, en restant dans des tailles raisonnables, il n'y a pas de limite à la taille d'une file d'attente LIFO. Le premier élément entré dans la pile doit attendre que tous les éléments arrivés après lui soient retirés de la mémoire tampon (assiettes retirées de la pile) jusqu'à ce qu'il soit le seul restant.

Priority queues are a bit harder for many people to imagine right off the bat. Think of a company that has one printer. Everyone uses that one printer. The print jobs are handled by department priority. Payroll has a higher priority (and thankfully so) than say, you, a programmer. You have a higher priority (and thankfully so) than the receptionist. So in short, the data that has a higher priority gets handled, and gets out of the queue, before data that has a lower priority. FIFO FIFO queues are easy to visualize in terms of data. A python list is an easy mental representation. Consider this list… [1,2,3,4,5,6,7,8,9,10]

Les files d'attente prioritaires sont un peu plus difficiles à comprendre du premier coup pour beaucoup de gens. Pensez à une entreprise qui possède une seule imprimante. Tout le monde utilise cette imprimante unique. Les travaux d'impression sont traités par ordre de priorité des départements. La paie a une priorité plus élevée (et heureusement) que, par exemple, vous, un programmeur. Vous avez une priorité plus élevée (et heureusement) que la réceptionniste. En bref, donc, les données qui ont une priorité plus élevée sont traitées et sortent de la file d'attente avant les données qui ont une priorité inférieure.

FIFO

Les files d'attente FIFO sont faciles à visualiser en termes de données. Une liste Python est une représentation mentale facile. Considérez cette liste…

  [1,2,3,4,5,6,7,8,9,10]

There are 10 items in the list. As a list, you access them by index. However, in a queue, you can't access the items by index. You have to deal with the next one in the line and the list isn't static. It's VERY dynamic. As we request the next item in the queue, it gets removed. So using the example above, you request one item from the queue. It returns the first item (1) and the queue then looks like this. [2,3,4,5,6,7,8,9,10] Request two more and you get 2, then 3, returned, and then the queue looks like this. [4,5,6,7,8,9,10] I'm sure you get the idea. Python provides a simple library, surprisingly enough, called Queue, that works well for small-to-medium sized queues, up to about 500 items. Here's a simple example to show it.

Il y a 10 articles dans la liste. En tant que liste, vous y accédez par l'index. Cependant, dans une file d'attente, vous ne pouvez pas accéder aux éléments par leur index. Vous devez traiter avec le prochain dans la file et la liste n'est pas figée. Elle est TRÈS dynamique. Lorsque nous demandons à accéder à l'élément suivant, il est retiré de la file d'attente. Donc, en utilisant l'exemple ci-dessus, vous demandez un élément de la file d'attente. Elle retourne le premier élément (1) et la file d'attente ressemble alors à ceci.

   [2,3,4,5,6,7,8,9,10]

Demandez-en deux de plus et vous obtenez 2, puis 3, et la file d'attente ressemble à ceci.

   [4,5,6,7,8,9,10]

Je suis sûr que vous voyez l'idée. Python fournit une simple bibliothèque, assez étonnamment appelée « Queue » [Ndt : qui signifie file d'attente], qui fonctionne bien pour des files d'attente de petite et moyenne taille, jusqu'à environ 500 éléments. Voici un exemple simple de démonstration :

import Queue fifo = Queue.Queue() for i in range(5): fifo.put(i) while not fifo.empty(): print fifo.get() In this example, we initialize the queue (fifo = Queue.Queue()) then put the numbers 0 through 4 into our queue (fifo.put(i)). We then use the internal method .get() to pull items off the queue until the queue is empty, .empty(). What is returned is 0,1,2,3,4. You can also set the maximum number of items that the queue can handle by initializing it with the size of the queue like this. fifo = Queue.Queue(300)

import Queue
fifo = Queue.Queue()
for i in range(5):
  fifo.put(i)
 
while not fifo.empty():
  print fifo.get()   

Dans cet exemple, on initialise la file d'attente (fifo = Queue.Queue()) puis on y place les nombres de 0 à 4 (fifo.put(i)). Nous utilisons ensuite la méthode interne .get() pour retirer des éléments de la file d'attente jusqu'à ce que la file d'attente soit vide, .empty(). Nous obtenons 0,1,2,3,4. Vous pouvez également définir le nombre maximal d'éléments que la file d'attente peut manipuler en l'initialisant avec la taille de la file d'attente comme cela.

fifo = Queue.Queue(300)

Once the maximum number of items have been loaded, the Queue blocks any additional entries going into the queue. This has a side effect of making the program look like it's “locked” up, though. The easiest way to get around this is to use the Queue.full() check. import Queue fifo = Queue.Queue(12) for i in range(13): if not fifo.full(): fifo.put(i) while not fifo.empty(): print fifo.get()

Une fois le nombre maximum d'éléments atteint, la file d'attente bloque toutes les entrées supplémentaires. Cela a cependant pour effet secondaire que le programme semble alors « planté ». La meilleure façon de contourner ce problème est d'utiliser la vérification Queue.full() qui indique si la file est pleine.

import Queue
fifo = Queue.Queue(12)
for i in range(13):
  if not fifo.full():
      fifo.put(i)
 
while not fifo.empty():
  print fifo.get()

In this case, the queue is set for a maximum of 12 items. As we put items into the queue, we start with '0' and get up to '11'. When we hit number 12, though, the buffer is already full. Since we check to see if the buffer is full before we try to put the item in, the last item is simply discarded. There are other options, but they can cause other side-effects, and we will address this in a future article. So, for the majority of the time, the bottom line is either use a queue with no limit or make sure you have more space in your queue than you will need. LIFO The Queue library also supports LIFO queues. We'll use the above list as a visual example. Setting up our queue, it looks like this: [1,2,3,4,5,6,7,8,9,10] Pulling three items from the queue, it then looks like this: [1,2,3,4,5,6,7]

Ici, la file d'attente est paramétrée à un maximum de 12 éléments. Lorsque nous ajoutons des éléments dans la file d'attente, nous commençons avec 0 et arrivons à 11. Mais lorsque nous atteignons le nombre 12, le tampon est déjà plein. Puisque nous vérifions si la mémoire tampon est pleine avant d'essayer d'ajouter un élément, le dernier élément est tout simplement rejeté.

Il existe d'autres options, mais elles peuvent causer d'autres effets secondaires, et nous aborderons la question dans un prochain article. Ainsi, la plupart du temps, la voie à suivre est soit d'utiliser une file d'attente sans aucune limite, soit de s'assurer que l'on prévoit plus d'espace dans la file d'attente que ce dont on aura besoin.

LIFO

La bibliothèque « Queue » prend également en charge les files d'attente LIFO. Nous allons utiliser la liste ci-dessus comme exemple visuel. Lors de la mise en place de notre file d'attente, elle ressemble à ceci :

   [1,2,3,4,5,6,7,8,9,10]

Si on retire trois éléments de la file d'attente, elle ressemble alors à ceci :

   [1,2,3,4,5,6,7]

Remember that in a LIFO queue, items are removed in a LAST-in FIRST-out order. Here's the simple example modified for a LIFO queue… import Queue lifo = Queue.LifoQueue() for i in range(5): lifo.put(i) while not lifo.empty(): print lifo.get() When we run it, we get “4,3,2,1,0”. As with the FIFO queue, you have the ability to set the size of the queue, and you can use the .full() check.

N'oubliez pas que dans une file d'attente LIFO, les éléments sont enlevés en commençant par le dernier entré. Voici l'exemple simple modifié pour une file d'attente LIFO…

import Queue
lifo = Queue.LifoQueue()
for i in range(5):
  lifo.put(i)
while not lifo.empty():
  print lifo.get()

Lorsqu'on l'exécute, on obtient 4,3,2,1,0.

Comme pour la file FIFO,vous pouvez régler la taille maximum de la file d'attente et utiliser la vérification .full() pour savoir si elle est pleine.

PRIORITY While it's not often used, a Priority queue can sometimes be helpful. It's pretty much the same as the other queue structures, but we need to pass a tuple that holds both the priority and the data. Here's an example using the Queue library: pq = Queue.PriorityQueue() pq.put((3,'Medium 1')) pq.put((4,'Medium 2')) pq.put((10,'Low')) pq.put((1,'high')) while not pq.empty(): nex = pq.get() print nex print nex[1]

PRIORITÉ

Même si elle n'est pas souvent utilisée, une file de priorité peut parfois être utile. C'est à peu près la même structure que pour les autres files d'attente, mais nous devons lui passer un tuple qui contient à la fois la priorité et les données. Voici un exemple en utilisant la bibliothèque « Queue » :

pq = Queue.PriorityQueue()
pq.put((3,'Moyenne 1'))
pq.put((4,'Moyenne 2'))
pq.put((10,'Basse'))
pq.put((1,'Haute'))
while not pq.empty():
  suivant = pq.get()
  print suivant
  print suivant[1]

First, we initialize the queue. Then we put four items into the queue. Notice we use the format (priority, data) to put our data. The library sorts our data in a ascending order based on the priority value. When we pull the data, it comes back as a tuple, just like we put it in. You can address by index the data. What we get back is… (1, 'high') high (3, 'Medium') Medium (4, 'Medium') Medium (10, 'Low') Low In our first two examples, we simply printed the data that comes out of our queue. That's fine for these examples, but in real-world programming, you probably need to do something with that information as soon as it comes out of the queue, otherwise it's lost. When we use the 'print fifo.get', we send the data to the terminal and then it's destroyed. Just something to keep in mind.

D'abord on initialise la file d'attente. Puis nous y plaçons quatre éléments. Remarquez que nous utilisons le format (priorité, données) pour placer nos données. La bibliothèque trie nos données selon un ordre basé sur la valeur de priorité. Quand nous extrayons les données, elles ressortent sous forme de tuple, comme lors de l'insertion. Vous pouvez utiliser l'indice pour accéder aux deux parties du tuple. Voici ce que nous obtenons…

(1, 'Haute')
Haute
(3, 'Moyenne 1')
Moyenne 1
(4, 'Moyenne 2')
Moyenne 2
(10, 'Basse')
Basse

Dans nos deux premiers exemples, nous avons simplement affiché les données qui sortent de notre file d'attente. C'est très bien pour ces exemples, mais dans le monde réel de la programmation, vous aurez probablement besoin de faire quelque chose avec cette information dès qu'elle sort de la file d'attente, sinon elle sera perdue. Lorsque nous utilisons « print fifo.get », nous envoyons les données vers le terminal puis elle sont détruites. Il faut juste garder ça à l'esprit.

Now let's use some of what we've already learned about tkinter to create a queue demo program. This demo will have two frames. The first will contain (to the user) three buttons. One for a FIFO queue, one for a LIFO queue, and one for a PRIORITY queue. The second frame will contain an entry widget, two buttons, one for adding to the queue, and one for pulling from the queue, and three labels, one showing when the queue is empty, one showing when the queue is full, and one to display what has been pulled from the queue. We'll also be writing some code to automatically center the window within the screen. Here's the beginning of the code. import sys from Tkinter import * import ttk import tkMessageBox import Queue class QueueTest: def init(self,master = None): self.DefineVars() f = self.BuildWidgets(master) self.PlaceWidgets(f) self.ShowStatus()

Maintenant, nous allons utiliser une partie de ce que nous avons déjà appris sur Tkinter pour créer un programme de démo de file d'attente. Cette démo aura deux cadres. Le premier contiendra (pour l'utilisateur) trois boutons. Un pour une file d'attente FIFO, un pour une file d'attente LIFO, et un autre pour une file de priorité. Le second cadre contiendra un widget champ de texte, deux boutons, l'un pour ajouter à la file d'attente et l'autre pour retirer de la file, et trois labels, l'un montrant quand la file est vide, l'un montrant quand la file est pleine, et un dernier pour afficher ce qui a été retiré de la file d'attente. Nous allons également écrire du code pour centrer automatiquement la fenêtre sur l'écran. Voici le début du code.

import sys
from Tkinter import *
import ttk
import tkMessageBox
import Queue
class TestFiles:
  def __init__(self,principale = None):
      self.DefinirVariables()
      f = self.ConstruireWidgets(principale)
      self.PlacerWidgets(f)
      self.AfficherStatut()

Here we have our imports and the beginning of our class. As before, we create the init routine with the DefineVars, BuildWidgets, and PlaceWidgets routines. We also have a routine called ShowStatus which will… well, show the status of our queue. def DefineVars(self): self.QueueType = '' self.FullStatus = StringVar() self.EmptyStatus = StringVar() self.Item = StringVar() self.Output = StringVar() # Define the queues self.fifo = Queue.Queue(10) self.lifo = Queue.LifoQueue(10) self.pq = Queue.PriorityQueue(10) self.obj = self.fifo

Ici, nous avons nos importations et le début de notre classe. Comme précédemment, nous créons la routine init avec les routines DefinirVariables, ConstruireWidgets et PlacerWidgets. Nous avons aussi une routine appelée AfficherStatut qui… affichera l'état de notre file d'attente.

 def DefinirVariables(self):
      self.TypeDeFile = ''
      self.StatutPlein = StringVar()
      self.StatutVide = StringVar()
      self.Element = StringVar()
      self.Sortie = StringVar()
      # Definit les files
      self.fifo = Queue.Queue(10)
      self.lifo = Queue.LifoQueue(10)
      self.pq = Queue.PriorityQueue(10)
      self.obj = self.fifo

We now create our DefineVars routine. We have four StringVar() objects, an empty variable called QueueType, and three queue objects - one for each of the types of queues that we are going to play with. We have set the maximum size of the queues at 10 for the purposes of the demo. We also have created an object called obj, and assigned it to the FIFO queue. When we select a queue type from the buttons, we will set this object to the queue that we want. This way, the queue is maintained when we switch to another queue type. def BuildWidgets(self,master): # Define our widgets frame = Frame(master) self.f1 = Frame(frame, relief = SUNKEN, borderwidth=2, width = 300, padx = 3, pady = 3 ) self.btnFifo = Button(self.f1, text = “FIFO” ) self.btnFifo.bind('<ButtonRelease-1>', lambda e: self.btnMain(1) ) self.btnLifo = Button(self.f1, text = “LIFO” ) self.btnLifo.bind('<ButtonRelease-1>', lambda e: self.btnMain(2) ) self.btnPriority = Button(self.f1, text = “PRIORITY” ) self.btnPriority.bind('<ButtonRelease-1>', lambda e: self.btnMain(3) )

Nous allons maintenant créer notre routine DefinirVariables. Nous avons quatre objets StringVar(), une variable vide appelé TypeDeFile, et trois objets file d'attente - un pour chaque type de file d'attente avec lesquels nous allons jouer. Nous avons fixé la taille maximale des files d'attente à 10 pour les besoins de la démo. Nous avons aussi créé un objet appelé obj auquel nous assignons la valeur FIFO. Lorsque nous sélectionnerons un type de file avec les boutons, nous mettrons dans cet objet le type de file d'attente que nous voulons. De cette façon, une file d'attente est conservée quand on passe à un autre type de file d'attente.

  def ConstruireWidgets(self,principale):
      # Definit nos widgets
      fenetre = Frame(principale)
      self.f1 = Frame(fenetre, 
          relief = SUNKEN,
          borderwidth=2,
          width = 300,
          padx = 3,
          pady = 3
      )
      self.btnFifo = Button(self.f1,
          text = "FIFO"
      )    
      
      self.btnFifo.bind('<Button-1>',
          lambda e: self.btnMain(1)
      )        
      self.btnLifo = Button(self.f1,
          text = "LIFO"
      )    
      self.btnLifo.bind('<ButtonRelease-1>',
          lambda e: self.btnMain(2)
      )
      self.btnPriority = Button(self.f1,
          text = "PRIORITY"
      )    
      self.btnPriority.bind('<ButtonRelease-1>',
          lambda e: self.btnMain(3)
      )

Here we start the widget definitions. We create our first frame, the three buttons, and their bindings. Notice we are using the same routine to handle the binding callback. Each button sends a value to the callback routine to denote which button was clicked. We could just as easily have created a dedicated routine for each button. However, since all three buttons are dealing with a common task, I thought it would be good to work them as a group. self.f2 = Frame(frame, relief = SUNKEN, borderwidth=2, width = 300, padx = 3, pady = 3 ) self.txtAdd = Entry(self.f2, width=5, textvar=self.Item ) self.txtAdd.bind('<Return>',self.AddToQueue) self.btnAdd = Button(self.f2, text='Add to Queue', padx = 3, pady = 3 ) self.btnAdd.bind('<ButtonRelease-1>',self.AddToQueue) self.btnGet = Button(self.f2, text='Get Next Item', padx = 3, pady = 3 ) self.btnGet.bind('<ButtonRelease-1>',self.GetFromQueue)

Ici nous commençons la définition des widgets. Nous créons notre premier cadre, les trois boutons et leurs fonctions de rappel. Notez que nous utilisons la même routine pour gérer les fonctions de rappel. Chaque bouton envoie une valeur à la routine de rappel pour indiquer quel bouton a été cliqué. Nous pourrions tout aussi bien pu créer une routine dédiée pour chaque bouton. Cependant, puisque les trois boutons gèrent une tâche commune, j'ai pensé qu'il serait bon de les considérer comme un groupe.

      self.f2 = Frame(fenetre, 
          relief = SUNKEN,
          borderwidth=2,
          width = 300,
          padx = 3,
          pady = 3
      )
      self.txtAdd = Entry(self.f2,
          width=5,
          textvar=self.Element
      )
      self.txtAdd.bind('<Return>',self.AjouterALaFile)
      self.btnAdd = Button(self.f2,
          text='Ajout dans la file',
          padx = 3,
          pady = 3
      )
      self.btnAdd.bind('<ButtonRelease-1>',self.AjouterALaFile)
      self.btnGet = Button(self.f2,
          text='Recupere element suivant',
          padx = 3,
          pady = 3
      )
      self.btnGet.bind('<ButtonRelease-1>',self.RecupererDansFile)

Next, we set up the second frame, the entry widget, and the two buttons. The only thing here that is out of the ordinary is the binding for the entry widget. Here we bind the self.AddToQueue routine to the <Return> key. This way, the user doesn't have to use the mouse to add the data. They can just enter the data into the entry widget, and press <Return> if they want to. self.lblEmpty = Label(self.f2, textvariable=self.EmptyStatus, relief=FLAT ) self.lblFull = Label(self.f2, textvariable=self.FullStatus, relief=FLAT ) self.lblData = Label(self.f2, textvariable=self.Output, relief = FLAT, font=(“Helvetica”, 16), padx = 5 ) return frame

Ensuite nous mettons en place le second cadre, le widget de saisie et les deux boutons. La seule chose ici qui sort de l'ordinaire est le rappel pour le widget de saisie. Ici nous associons la routine self.AjouterALaFile à la touche « Return » (Entrée). De cette façon, l'utilisateur n'a pas à utiliser la souris pour ajouter les données. Il peut simplement entrer les données dans la zone de saisie et appuyer sur Entrée.

      self.lblEmpty = Label(self.f2,
          textvariable=self.StatutVide,
          relief=FLAT
      )
      self.lblFull = Label(self.f2,
          textvariable=self.StatutPlein,
          relief=FLAT
      )
      self.lblData = Label(self.f2,
          textvariable=self.Sortie,
          relief = FLAT,
          font=("Helvetica", 16),
          padx = 5
      )
      return fenetre

Here's the last three widget definitions. All three are labels. We set the textvariable attribute to the variables we defined earlier. If you remember, when that variable changes, so does the text in the label. We also do something a bit different on the lblData label. We will use a different font to make it stand out when we display the data pulled from the queue. Remember that we have to return the frame object so it can be used in the PlaceWidget routine. def PlaceWidgets(self, master): frame = master # Place the widgets frame.grid(column = 0, row = 0) l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 0, row = 0) l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 1, row = 0) l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 2, row = 0) l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 3, row = 0) l = Label(frame,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 4, row = 0) self.f1.grid(column = 0,row = 1,sticky='nsew',columnspan=5,padx = 5,pady = 5) l = Label(self.f1,text='',width = 25,anchor = 'e').grid(column = 0, row = 0) self.btnFifo.grid(column = 1,row = 0,padx = 4) self.btnLifo.grid(column = 2,row = 0,padx = 4) self.btnPriority.grid(column = 3, row = 0, padx = 4)

Voici les trois dernières définitions de widgets. Toutes les trois sont des étiquettes. Nous réglons l'attribut textvariable des variables que nous avons définies plus tôt. Si vous vous souvenez, lorsque cette variable change, le texte de l'étiquette changera aussi. Nous faisons aussi quelque chose d'un peu différent sur ​​l'étiquette lblData. Nous allons utiliser une police différente pour faire ressortir l'affichage des données extraites de la file d'attente. Rappelez-vous que nous devons retourner l'objet fenêtre de sorte qu'il puisse être utilisé dans la routine PlacerWidgets.

  def PlacerWidgets(self, principale):
      fenetre = principale
      # Place les widgets
      fenetre.grid(column = 0, row = 0)
      l = Label(fenetre,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 0, row = 0)
      l = Label(fenetre,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 1, row = 0)
      l = Label(fenetre,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 2, row = 0)
      l = Label(fenetre,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 3, row = 0)
      l = Label(fenetre,text='',relief=FLAT,width = 15, anchor = 'e').grid(column = 4, row = 0)
                                     
      self.f1.grid(column = 0,row = 1,sticky='nsew',columnspan=5,padx = 5,pady = 5) 
      l = Label(self.f1,text='',width = 25,anchor = 'e').grid(column = 0, row = 0)
      self.btnFifo.grid(column = 1,row = 0,padx = 4)
      self.btnLifo.grid(column = 2,row = 0,padx = 4)
      self.btnPriority.grid(column = 3, row = 0, padx = 4)

This is the beginning of the PlaceWidgets routine. Notice here that we put five empty labels at the very top of the root window. I'm doing this to set spacing. This is an easy way to “cheat” and make your window placement much easier. We then set the first frame, then another “cheater” label, then the three buttons. self.f2.grid(column = 0,row = 2,sticky='nsew',columnspan=5,padx = 5, pady = 5) l = Label(self.f2,text='',width = 15,anchor = 'e').grid(column = 0, row = 0) self.txtAdd.grid(column=1,row=0) self.btnAdd.grid(column=2,row=0) self.btnGet.grid(column=3,row=0) self.lblEmpty.grid(column=2,row=1) self.lblFull.grid(column=3,row = 1) self.lblData.grid(column = 4,row = 0) Here we place the second frame, another “cheater” label, and the rest of our widgets. def Quit(self): sys.exit()

C'est le début de la routine PlacerWidgets. Remarquez que nous avons mis ici cinq étiquettes vides tout en haut de la fenêtre racine. Je fais cela pour régler l'espacement. C'est un moyen facile de « tricher » pour faciliter le placement de la fenêtre. Nous réglons ensuite le premier cadre, puis une autre étiquette « de triche », puis les trois boutons.

      self.f2.grid(column = 0,row = 2,sticky='nsew',columnspan=5,padx = 5, pady = 5) 
      l = Label(self.f2,text='',width = 15,anchor = 'e').grid(column = 0, row = 0)
      self.txtAdd.grid(column=1,row=0)
      self.btnAdd.grid(column=2,row=0)
      self.btnGet.grid(column=3,row=0)
      self.lblEmpty.grid(column=2,row=1)
      self.lblFull.grid(column=3,row = 1)
      self.lblData.grid(column = 4,row = 0)

Nous plaçons maintenant le deuxième cadre, encore une étiquette « de triche » puis le reste de nos widgets.

  def Quitter(self):
      sys.exit()

Next we have our “standard” quit routine which simply calls sys.exit(). def btnMain(self,p1): if p1 == 1: self.QueueType = 'FIFO' self.obj = self.fifo root.title('Queue Tests - FIFO') elif p1 == 2: self.QueueType = 'LIFO' self.obj = self.lifo root.title('Queue Tests - LIFO') elif p1 == 3: self.QueueType = 'PRIORITY' self.obj = self.pq root.title('Queue Tests - Priority') print self.QueueType self.ShowStatus()

Ensuite nous avons notre routine « standard » pour quitter l'application, qui appelle simplement sys.exit().

  def btnMain(self,p1):
      if p1 == 1:
          self.TypeDeFile = 'FIFO'
          self.obj = self.fifo
          root.title('Tests Files - FIFO')
      elif p1 == 2:
          self.TypeDeFile = 'LIFO'
          self.obj = self.lifo
          root.title('Tests Files - LIFO')
      elif p1 == 3:
          self.TypeDeFile = 'PRIORITY'
          self.obj = self.pq
          root.title('Tests Files - Priorite')
      print self.TypeDeFile
      self.AfficherStatut()

Now our main button callback routine, btnMain. Remember we are sending in (through the p1 parameter) which button was clicked. We use the self.QueueType variable as a reference to which queue type we are dealing with, then we assign self.obj to the proper queue, and finally change the title of our root window to display the queue type we are using. After that, we print the queue type to the terminal window (you don't really have to do that), and call the ShowStatus routine. Next we'll make the ShowStatus routine. def ShowStatus(self): # Check for Empty if self.obj.empty() == True: self.EmptyStatus.set('Empty') else: self.EmptyStatus.set() # Check for Full if self.obj.full() == True: self.FullStatus.set('FULL') else: self.FullStatus.set()

Maintenant, notre routine principale de rappel pour les boutons, btnMain. Rappelez-vous que nous lui envoyons (via le paramètre p1) quel bouton a été cliqué. Nous utilisons la variable self.TypeDeFile en référence au type de file d'attente que nous sommes en train de gérer, puis nous assignons à self.obj la file d'attente appropriée et, enfin, changeons le titre de notre fenêtre racine pour afficher le type de file d'attente que nous utilisons. Après cela, nous affichons le type de file dans le terminal (vous n'êtes pas obligé de faire cela), puis appelons la routine AfficherStatut. Maintenant nous allons écrire la routine AfficherStatut.

  def AfficherStatut(self):
      # verifie si vide
      if self.obj.empty() == True:
          self.StatutVide.set('Vide')
      else:
          self.StatutVide.set('')
      # verifie si plein
      if self.obj.full() == True:
          self.StatutPlein.set('Plein')
      else:
          self.StatutPlein.set('')

As you can see, it's pretty simple. We set the label variables to their proper state so they display if the queue we are using is either full, empty, or somewhere in between. def AddToQueue(self,p1): temp = self.Item.get() if self.QueueType == 'PRIORITY': commapos = temp.find(',') if commapos == -1: print "ERROR" tkMessageBox.showerror('Queue Demo', 'Priority entry must be in format\r(priority,data)') else: self.obj.put(self.Item.get()) elif not self.obj.full(): self.obj.put(self.Item.get()) self.Item.set('') self.ShowStatus()

Comme vous pouvez le voir, c'est assez simple. Nous réglons les variables d'étiquettes à leur bon état afin qu'elles affichent si la file d'attente que nous utilisons est pleine, vide, ou quelque part entre les deux.

  def AjouterALaFile(self,p1):
      temp = self.Element.get()
      if self.TypeDeFile == 'PRIORITY':
          commapos = temp.find(',')
          if commapos == -1:
              print "ERREUR"
              tkMessageBox.showerror('Demo File',
                  'Un element Priority doit etre au format\r(priorite,valeur)')
          else:
              self.obj.put(self.Element.get())
      elif not self.obj.full():
          self.obj.put(self.Element.get())
      self.Element.set('')
      self.AfficherStatut()

The AddToQueue routine is also fairly straight-forward. We get the data from the entry box using the .get() function. We then check to see if the current queue type is a priority queue. If so, we need to make sure it's in the correct format. We do that by checking for the presence of a comma. If it isn't, we complain to the user via an error message box. If everything seems correct, we then check to see if the queue that we are currently using is full. Remember, if the queue is full, the put routine is blocked and the program will hang. If everything is fine, we add the item to the queue and update the status. def GetFromQueue(self,p1): self.Output.set('') if not self.obj.empty(): temp = self.obj.get() self.Output.set("Pulled {0}".format(temp)) self.ShowStatus()

La routine AjouterALaFile est également assez simple. Nous récupérons les données du champ de saisie en utilisant la fonction .get(). Nous vérifions ensuite si le type courant de file d'attente est une file de priorité. Si c'est le cas, nous devons nous assurer que le format de saisie est correct. Nous vérifions cela en testant la présence d'une virgule. S'il n'y en a pas, nous prévenons l'utilisateur via une boîte de message d'erreur. Si tout semble correct, nous vérifions ensuite si la file d'attente que nous utilisons actuellement est pleine. N'oubliez pas, si la file est pleine, la routine d'insertion est bloquée et le programme va planter. Si tout va bien, nous ajoutons l'élément à la file d'attente et mettons à jour le statut.

  def RecupererDansFile(self,p1):
      self.Sortie.set('')
      if not self.obj.empty():
          temp = self.obj.get()
          self.Sortie.set("Sorti {0}".format(temp))
      self.AfficherStatut()

The GetFromQueue routine is even easier. We check to see if the queue is empty so as not to run into a blocking issue, and, if not, we pull the data from the queue, show the data, and update the status. if name == 'main': def Center(window): # Get the width and height of the screen sw = window.winfo_screenwidth() sh = window.winfo_screenheight() # Get the width and height of the window rw = window.winfo_reqwidth() rh = window.winfo_reqheight() xc = (sw-rw)/2 yc = (sh-rh)/2 window.geometry(“%dx%d+%d+%d”%(rw,rh,xc,yc)) window.deiconify()

La routine RecupererDansFile est encore plus facile. Nous vérifions si la file est vide afin de ne pas nous heurter à un problème de blocage et, si ce n'est pas le cas, nous retirons les données de la file d'attente, l'affichons, et mettons à jour le statut.

if name == 'main':

  def Centrer(window):
      # recupere largeur et hauteur de l'ecran
      largeurE = window.winfo_screenwidth()
      hauteurE = window.winfo_screenheight()        
      # recupere largeur et hauteur de la fenetre
      largeurF = window.winfo_reqwidth()
      hauteurF = window.winfo_reqheight()
      xc = (largeurE-largeurF)/2
      yc = (hauteurE-hauteurF)/2
      window.geometry("%dx%d+%d+%d"%(largeurF,hauteurF,xc,yc))
      window.deiconify()

We are getting to the end of our application. Here is the center window routine. We first get the screen width and screen height of the screen we are on. We then get the width and height of the root window by using the winfo_reqwidth() and winfo_reqheight() routines built into tkinter. These routines, when called at the right time, will return the width and height of the root window based on the widget placement. If you call it too early, you'll get data, but it won't be what you really need. We then subtract the required window width from the screen width, and divide it by two, and do the same thing for the height information. We then use that information to set the geometry call. In MOST instances, this works wonderfully. However, there might be times that you need to set the required width and height by hand. root = Tk() root.title('Queue Tests - FIFO') demo = QueueTest(root) root.after(3,Center,root) root.mainloop()

Nous arrivons à la fin de notre application. Voici la routine de centrage de fenêtre. Nous récupérons d'abord la largeur et la hauteur de l'écran. Nous récupérons ensuite la largeur et la hauteur de la fenêtre racine à l'aide des routines winfo_reqwidth() et winfo_reqheight() intégrées à tkinter. Ces routines, lorsqu'elles sont appelées au bon moment, retourneront la largeur et la hauteur de la fenêtre racine en tenant compte du placement des widgets. Si vous l'appelez trop tôt, vous obtiendrez des valeurs, mais pas celles dont vous avez vraiment besoin. Nous soustrayons ensuite la largeur de la fenêtre de la largeur de l'écran, et divisons cela par 2, puis nous faisons la même chose pour la hauteur. Nous utilisons alors ces informations dans l'appel de la fonction geometry. La plupart du temps, cela fonctionne à merveille. Toutefois, il pourrait y avoir des moments où vous aurez besoin de définir la largeur et la hauteur à la main.

  root = Tk()
  root.title('Tests File - FIFO')
  demo = TestFiles(root)
  root.after(3,Centrer,root)
  root.mainloop()

Finally, we instantiate the root window, set the base title, instantiate the QueueTest class. We then call root.after, which waits x number of milliseconds (in this case 3) after the root window is instantiated, and then calls the Center routine. This way, the root window has been completely set up and is ready to go, so we can get the root window width and height. You might have to tweak the delay time a bit. Some machines are much faster than others. 3 works fine on my machine, your mileage may vary. Last but not least, we call the root window mainloop to get the application to run. As you play with the queues, notice that if you put some data in one queue (let's say the FIFO queue) then switch to another queue (let's say the LIFO queue), the data that was put into the FIFO queue is still there and waiting for you. You can completely or partially fill all three queues, then start playing with them. Well, that's it for this time. Have fun with your queues. The QueueTest code can be found at http://pastebin.com/5BBUiDce.

Enfin, nous instancions la fenêtre racine, définissons le titre de base et instancions la class TestFiles. Nous appelons ensuite root.after, qui attend un nombre x de millisecondes (dans ce cas 3), après que la fenêtre racine soit instanciée, puis appelle la routine Centrer. De cette façon, la fenêtre racine a été complètement paramétrée et est prête à s'afficher, donc nous pouvons obtenir sa largeur et sa hauteur. Vous pourriez avoir à ajuster légèrement le temps de retard. Certaines machines sont beaucoup plus rapides que d'autres. 3 fonctionne très bien sur ma machine, votre réglage peut varier. Enfin nous appelons la boucle principale de la fenêtre racine pour exécuter l'application.

Pendant que vous jouez avec les files d'attente, notez que si vous mettez des données dans une file d'attente (disons la file d'attente FIFO), puis passez à une autre file d'attente (disons la file d'attente LIFO), les données qui ont été placées dans la file FIFO sont toujours là et vous attendent. Vous pouvez complètement ou partiellement remplir les trois files d'attente, puis commencer à jouer avec.

Eh bien, c'est tout pour cette fois-ci. Amusez-vous avec vos files d'attente. Le code de TestFiles peut être trouvé ici : http://pastebin.com/MKLTmSES

issue53/tutopython.txt · Dernière modification : 2011/11/17 16:00 de majordom