0

これはあなたが見たことのある最悪のコードの1つだと確信していますが、これが私の最初のオブジェクト指向プログラムです。 このプログラムは、Arduinoと通信して、ソーラーアレイとバッテリに関する情報を収集する必要があります。また、いくつかのインバータなどを自動的に管理する必要があります。 私はコードを少し読みやすくするためにGUIの大部分を削除しましたが、それはまだかなり大きいです。 私がコードしようとしたことは、シリアル通信が開始されてからGUIのパラメータを変更できるようにすることです。バックグラウンドで動作し、データを収集または送信する新しいスレッドを開くことでこれを実現しようとしました。 実際には、シリアル通信が開始されるとすぐにGUIがフリーズし、しばらくしてからすべてが狂ってしまいます。 スレッド内に印刷を追加して、通信が開始されているかどうか、実際にはPythonがシリアルポートから情報を収集する前に確認します。GUIスレッドを開くときにフリーズする

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

- それは、キー/マウスイベントを取得し、ウィジェットに送信し、ウィジェットの変更データを、ウィジェットを再描画します。 'root.after(millisecond、function_name)'を使って定期的にいくつかの関数を実行し、 'while'ループを"シミュレート "することができます。あるいは、ループで 'root.update()'を使ってmainloopに1つのループを強制することもできます。 – furas

+0

しかし、私が新しいスレッドに配置した場合、なぜwhileループがmailnoopをブロックするのですか? 理論上のあなたのコマンドであなたの答えを理解できたら、スレッドを正しく取り除くことができましたか? –

+0

簡単な例:[シリアルをtkinterで読む](https://github.com/furas/my-python-codes/blob/master/tkinter/read-serial-port/main.py) – furas

答えて

2

あなたは間違った方法でスレッドを実行します。今、あなたは

threading.Thread(target=comunicazione(porta)) 

を持っていますが、関数名はtarget=を期待 - それは()と引数なしを意味します。

したがって、メインスレッドではcomunicazione(porta)を通常の関数として実行し、関数が何かを返すと、target=に割り当てられます。しかし、関数は決して停止せず、メインスレッドをブロックしません。

lambdaを使用すると、引数なしで関数を作成し、変数に代入することができます。

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

しかし、your_thread.start()を使用してスレッドを実行する必要があります。あなたはそれのブロック `Tkinterではeverethingんmainloop`(および他のGUI)ので` while`ループを使用することはできません

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

これで動作します。ありがとうございました! 'avvia_seriale(porta)'例外に関する警告を注意すべきでしょうか、それとも無視できますか? –

+0

あなたは少なくともそれを 'print()'することができます - 時にはそれが役に立つ情報かもしれません。 – furas

関連する問題