0

Я уверен, что это один из худших кодов, которые вы когда-либо видели, но это моя первая объектно-ориентированная программа. Эта программа должна общаться с Arduino для сбора информации о солнечной батарее и некоторых батареях. Он также будет автоматически управлять некоторыми инверторами и так далее. Я удалил большую часть графического интерфейса, чтобы сделать код немного легче читать, но он по-прежнему довольно большой. То, что я пытался скомпоновать, - это то, что после начала последовательной связи я могу изменить параметры в графическом интерфейсе, я попытался реализовать это, открыв новый поток, который работает в фоновом режиме и собирает или отправляет данные. Что на самом деле происходит, так это то, что, как только начинается последовательная связь, GUI замерзает, и через некоторое время все царапины. Я добавил печать внутри потока, чтобы проверить, начинаются ли связи и на самом деле до того, как python chrashes собирает некоторую информацию из последовательного порта.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

вы не можете использовать 'while' цикл, поскольку он блокирует' mainloop' который не everething в Tkinter (и любая другая GUI) - он получает событие ключа/мыши, отправляет его виджеты, изменения данных, виджеты, и перерисовывает виджетов. Вы можете использовать 'root.after (millisecond, function_name)', чтобы периодически запускать некоторую функцию и «имитировать» цикл while. Или вы можете использовать 'root.update()' в своем цикле, чтобы заставить mainloop делать один цикл. – 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() для запуска потока, т.е.

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

Теперь он работает. Спасибо! Должен ли я заботиться о предупреждении об исключении 'avvia_seriale (porta)', или я могу его игнорировать? –

+0

вы могли бы хотя бы 'print()' it - иногда это может быть полезной информация. – furas

Смежные вопросы