0

Je suis sûr que c'est l'un des pires codes que vous avez jamais vu, mais c'est mon premier programme orienté objet. Ce programme devrait communiquer avec un Arduino pour collecter des informations sur un générateur solaire et certaines batteries. Il faudra aussi gérer automatiquement certains onduleurs et ainsi de suite. J'ai enlevé la plus grande partie de l'interface graphique pour rendre le code un peu plus facile à lire mais il reste assez gros. Ce que j'ai essayé de coder est quelque chose qui une fois que la communication série commence à changer les paramètres sur l'interface graphique, j'ai essayé de le réaliser en ouvrant un nouveau thread qui fonctionne en arrière-plan et recueille ou envoie les données. Ce qui se passe en réalité, c'est que dès que la communication série commence, l'interface graphique se bloque et après un certain temps, tout se produit. J'ai ajouté une impression à l'intérieur du thread pour vérifier si les communications commençaient et en fait, avant que les python chrashes, certaines informations soient collectées à partir du port série.Freez GUI lors de l'ouverture d'un fil

import Tkinter 
import tkMessageBox 
import ttk 
import serial 
import sys 
import glob 
import threading 
from time import sleep 


class PaginaPrincipale(Tkinter.Tk, threading.Thread): 
    dati_in = None 
    dati_out = None 

    def __init__(self, parent): 
     Tkinter.Tk.__init__(self, parent) 
     self.parent = parent 
     si1 = Tkinter.IntVar() 
     au1 = Tkinter.IntVar() 
     si2 = Tkinter.IntVar() 
     au2 = Tkinter.IntVar() 

     self.grid() 

     # those classes will manage the auto function 
     def manuale(variable): 
      if variable == 1: 
       print(si1.get()) 
      if variable == 2: 
       print(si2.get()) 

     def automatico(variable): 
      if variable == 1: 
       print(au1.get()) 
      if variable == 2: 
       print(au2.get()) 

     # this class manages the serial connection, it scans for the available ports 
     # and when the user select the desired one it should open it and start a thread 
     # I still haven't implemented the update of the GUI 
     def connetti(): 

      # Here I extract the clicked value on the listbox 
      def selezione(evt): 
       w = evt.widget 
       index = int(w.curselection()[0]) 
       value = w.get(index) 
       scelta_box.config(text=value) 

      # Here I try to open the selected port and to start a new thread which keeps exchanging 
      # information with the microcontroller (Arduino) 
      def avvia_seriale(porta): 
       try: 
        print(porta) 
        pagina_connessione.destroy() 
        threading.Thread(target=comunicazione(porta)) 

       except: 
        # Here PiCharm gives me a warning: too broad exception clause 
        tkMessageBox.showerror('Serial port', 'Can''t open the selected serial port') 
        pass 

      # here I will place all the serial communication statements 
      def comunicazione(porta): 
       porta_seriale = serial.Serial(porta) 
       while porta_seriale.isOpen(): 
        porta_seriale.write(1) 
        sleep(.1) 
        self.dati_in = porta_seriale.readline() 
        sleep(.1) 
        print self.dati_in 
       pass 

      # Here I scan for available ports and I put them inside the listbox 
      if sys.platform.startswith('win'): 
       ports = ['COM%s' % (i + 1) for i in range(256)] 
      elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): 
       # this excludes your current terminal "/dev/tty" 
       ports = glob.glob('/dev/tty[A-Za-z]*') 
      elif sys.platform.startswith('darwin'): 
       ports = glob.glob('/dev/tty.*') 
      else: 
       raise EnvironmentError('Unsupported platform') 

      result = [] 
      for port in ports: 
       try: 
        s = serial.Serial(port) 
        s.close() 
        result.append(port) # il metodo append() aggiunge alla lista result l'ultimo termine trovato 
       except (OSError, serial.SerialException): 
        pass 

      # I open a new toplevel so that when I choose and open the serial port I close it and nothing remains 
      # on the main page 
      pagina_connessione = Tkinter.Toplevel() 
      pagina_connessione.title('Gestione connessione') 

      descrizione_scelte = Tkinter.Label(pagina_connessione, text='Lista scelte:', justify='left') 
      descrizione_scelte.grid(column=0, row=0, sticky='W') 
      lista_scelte = Tkinter.Listbox(pagina_connessione, height=len(result), selectmode='single') 
      contatore = len(result) 
      for item in result: 
       lista_scelte.insert(contatore, item) 
       contatore += 1 

      if contatore == 0: 
       lista_scelte.insert(0, 'Nessuna porta seriale') 

      lista_scelte.grid(column=0, row=1) 
      lista_scelte.bind('<<ListboxSelect>>', selezione) 

      bottone_connessione = Tkinter.Button(pagina_connessione, text='Connetti!', 
               command=lambda: avvia_seriale(scelta_box.cget("text"))) 
      bottone_connessione.grid(column=1, row=1) 

      scelta_box = Tkinter.Label(pagina_connessione, width=15, height=1, borderwidth=3, background='blue') 
      scelta_box.grid(column=0, row=2) 

      pagina_connessione.mainloop() 

     # 
     # 
     # This is the main GUI 
     # 
     # 
     frame_batteria1 = Tkinter.Frame(self, borderwidth=2, bg="black") 
     frame_batteria1.grid(column=0, row=0, sticky='news') 

     self.descrittore_v_b_1 = Tkinter.Label(frame_batteria1, text="V Batteria 1", font=("Helvetica", 8), 
               justify='center') 
     self.descrittore_v_b_1.grid(column=0, row=0, sticky='news') 
     self.descrittore_i_b_1 = Tkinter.Label(frame_batteria1, text="I Batteria 1", font=("Helvetica", 8), 
               justify='center') 
     self.descrittore_i_b_1.grid(column=1, row=0, sticky='NEWS') 

     self.vbatteria1 = Tkinter.Scale(frame_batteria1, bd=4, troughcolor='blue', resolution=0.1, state='disabled', 
             from_=15, to=0) 
     self.vbatteria1.grid(column=0, row=1, sticky='NEWS') 
     self.ibatteria1 = Tkinter.Scale(frame_batteria1, bd=4, troughcolor='blue', resolution=0.1, state='disabled', 
             from_=10, to=0) 
     self.ibatteria1.grid(column=1, row=1, sticky='NEWS') 

     self.descrittore_inverter1 = Tkinter.Label(self, text="Inverter 1", font=("Helvetica", 8), justify='left') 
     self.descrittore_inverter1.grid(column=0, row=3, sticky='NEWS') 

     self.scelte_manuali_inverter1 = Tkinter.Radiobutton(self, text="Acceso", variable=si1, value=1, 
                  command=lambda: manuale(1)) 
     self.scelte_manuali_inverter1.grid(column=0, row=4, sticky='NEWS') 
     self.scelte_manuali_inverter1 = Tkinter.Radiobutton(self, text="Spento", variable=si1, value=0, 
                  command=lambda: manuale(1)) 
     self.scelte_manuali_inverter1.grid(column=0, row=5, sticky='NEWS') 

     self.scelta_automatica_inverter1 = Tkinter.Checkbutton(self, text="Automatico", variable=au1, onvalue=1, 
                   offvalue=0, command=lambda: automatico(1)) 
     self.scelta_automatica_inverter1.grid(column=2, row=4, sticky='NEWS') 

     # 
     # 
     # separators 
     # 
     # 
     ttk.Separator(self, orient='horizontal').grid(row=6, columnspan=8, sticky='EW') 
     ttk.Separator(self, orient='vertical').grid(row=2, column=3, rowspan=4, sticky='NS') 

     self.gestisci_connessione = Tkinter.Button(self, text="Connetti!", command=connetti) 
     self.gestisci_connessione.grid(row=7, column=6, sticky='EW') 


if __name__ == "__main__": 
    applicazione = PaginaPrincipale(None) 
    applicazione.title('Pannello di controllo') 
    applicazione.mainloop() 
+0

vous ne pouvez pas utiliser la boucle 'while' car il bloque' mainloop' qui n'everething dans Tkinter (et tout autre GUI) - il devient événement clé/souris, envoie aux widgets, les données de changements dans les widgets, et redessine les widgets. Vous pouvez utiliser 'root.after (millisecond, function_name)' pour exécuter une fonction périodiquement et "simuler" 'while' en boucle. Ou vous pouvez utiliser 'root.update()' dans votre boucle pour obliger mainloop à faire une boucle. – furas

+0

Mais pourquoi la boucle while bloque le mailnoop si je l'ai placé dans un nouveau thread? Si j'ai compris votre réponse avec votre commande en théorie, je pourrais supprimer le fil droit? –

+0

exemple simple: [lecture série dans tkinter] (https://github.com/furas/my-python-codes/blob/master/tkinter/read-serial-port/main.py) – furas

Répondre

2

Vous exécutez le thread dans le mauvais sens. Maintenant, vous avez

threading.Thread(target=comunicazione(porta)) 

Mais target= s'attend nom de la fonction - cela signifie sans () et arguments.

Donc, vous exécutez comunicazione(porta) comme fonction normale dans le thread principal et lorsque la fonction renvoie quelque chose, il sera affecté à target=. Mais la fonction ne s'arrête jamais et bloque le thread principal.

Vous pouvez utiliser lambda pour créer une fonction sans argument et l'affecter à une variable.

threading.Thread(target=lambda:comunicazione(porta)) 

Mais vous devez utiliser your_thread.start() pour exécuter thread.

t = threading.Thread(target=lambda:comunicazione(porta)) 
t.start() 
+0

Maintenant, cela fonctionne. Je vous remercie! Devrais-je prendre en charge l'avertissement sur l'exception 'avvia_seriale (porta)' ou l'ignorer? –

+0

vous pouvez au moins 'print()' it - parfois cela peut être utile. – furas