GUI在打开线程时Freezzes

时间:2016-11-24 09:09:45

标签: python multithreading user-interface arduino serial-communication

我确定这是你见过的最糟糕的代码之一,但这是我的第一个面向对象的程序。 该程序应与Arduino通信,以收集有关太阳能电池阵列和一些电池的信息。它还将自动管理一些逆变器等。 我删除了GUI的大部分内容,使代码更易于阅读,但仍然非常大。 我尝试编码的是一旦串行通信开始让我更改GUI上的参数,我试图通过打开一个在后台工作并收集或发送数据的新线程来实现这一点。 实际发生的是,一旦串行通信启动,GUI就会冻结,一段时间后一切都会崩溃。 我在线程中添加了一个打印来检查通信是否开始,实际上是在python chrashes之前从串口收集了一些信息。

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()

1 个答案:

答案 0 :(得分: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()