Какой питонный способ заставить работать сокет-клиента с помощью tkinter? - PullRequest
2 голосов
/ 19 апреля 2019

Я пытаюсь настроить клиент GUI / TCP, который отображает некоторые полученные данные с сервера TCP в Python.Для этого я использую модуль tkinter.

У меня есть 2 вопроса о коде, который я выкладываю ниже:

  1. Как мне реализовать это с помощью питона?

  2. Как использовать многопоточное соединение (), чтобы дождаться заполненного self.inboundMessage, прежде чем продолжить выполнение приложения?

(* В комментариях некоторые вещи, которые я пробовал)

import tkinter as tk
from tkinter import *
from tkinter import ttk

import socket

import threading
from threading import *

import json             # Import json module
import logging          # Import logging module
import random
import time


logging.basicConfig(level=logging.DEBUG, format='[%(threadName)-10s] %(message)s',)




class MyLabel(tk.Label):

    def __init__(self, root, func, **kwargs):
        tk.Label.__init__(self, root, **kwargs)
        self.func = func
        self.update()


    def update(self):
        self.configure(text=str(self.func()))
        self.after(500, self.update)


# To be implemented as an interface
class MyFrame(ttk.Frame):

    def __init__(self, root):
        super().__init__(root)


    def read(self):
        pass


    def write(self):
        pass


    def update(self):
        pass



class MyInfoFrame(MyFrame):

    def __init__(self, root, func):
        super().__init__(root)

        dictionary = func()
        self.func = func
        self.values = {}

        row = 0

        ###############
        print('**************')
        print(dictionary)
        ##############

        try:

            for sensor in dictionary['sensor']:
                tk.Label(self, text = sensor['name']).grid(row=row, column=0)
                self.values[sensor['name']] = tk.Label(self, text = sensor['value'], relief=SUNKEN, width=5)
                self.values[sensor['name']].grid(row=row, column=1)
                tk.Label(self, text = sensor['unit']).grid(row=row, column=2)
                row = row + 1

            for handler in dictionary['handler']:
                tk.Label(self, text = handler['name']).grid(row=row, column=0)
                self.values[handler['name']] = tk.Label(self, text = handler['status'], relief=SUNKEN, width=5)
                self.values[handler['name']].grid(row=row,column=1)
                row = row + 1

        except:

            print('MyInfoFrame.__init__ reading error...')
            pass


        self.update()



    def update(self):

        dictionary = self.func()

        try:

            #################
            print('\n **************')
            print(dictionary['sensor'])
            print('**************\n')
            #################

            for sensor in dictionary['sensor']:
                self.values[sensor['name']].configure(text=sensor['value'])

            for handler in dictionary['handler']:
                self.values[handler['name']].configure(text=handler['status'])

        except:

            #print('MyInfoFrame.update reading error...')
            logging.error('MyInfoFrame.update reading error...')
            pass


        self.after(500, self.update)



class MySettingsFrame(MyFrame):

    def __init__(self, root, **kwargs):
        super().__init__(root)

        self.systemOn = False
        self.getOutboundMessage = kwargs['func_2']
        self.outboundMessage = self.getOutboundMessage()

        tk.Label(self, text='Encendido').grid(row=0,column=0)
        ttk.Radiobutton(self, text='ON', value='ON', command=self.setSystemOn).grid(row=0,column=1)
        offButton = ttk.Radiobutton(self, text='OFF', value='OFF', command=self.setSystemOff)
        offButton.grid(row=0,column=2)
        offButton.invoke()


        self.scaleValue = 15.0
        self.scale = ttk.Scale(self, orient=HORIZONTAL, length=200, from_=self.scaleValue, to=35.0, command=self.setScaleValue)
        self.scale.set(self.scaleValue)
        self.scale.grid(row=2,column=1)


        tk.Label(self, text='Temp. Consigna').grid(row=1,column=0)
        MyLabel(self, func=self.getScaleValue, relief=SUNKEN, width=5).grid(row=1,column=1)
        tk.Label(self, text='ºC').grid(row=1,column=2)

        self.update()


    def setSystemOn(self):
        self.systemOn = True


    def setSystemOff(self):
        self.systemOn = False


    def setScaleValue(self, *args):
        self.scaleValue = round(self.scale.get(), 1)


    def getScaleValue(self):
        return self.scaleValue


    def write(self):
        self.outboundMessage['timestamp'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
        self.outboundMessage['origin'] = 'front-end'
        self.outboundMessage['action'] = 'set'
        self.outboundMessage['setup'] = self.scaleValue

        if self.systemOn:
            self.outboundMessage['system'] = 'ON'
        else:
            self.outboundMessage['system'] = 'OFF'


    def update(self):
        self.write()
        self.after(500, self.update)



class MyAdvancedFrame(MyFrame):

    def __init__(self, root, **kwargs):
        super().__init__(root)

        self.tempIntSimulated = False
        self.tempExtSimulated = False

        self.getInboundMessage = kwargs['func_1']
        self.getOutboundMessage = kwargs['func_2']

        self.tempInt = self.getInboundMessage()['sensor'][0]['value']
        self.tempExt = self.getInboundMessage()['sensor'][1]['value']

        self.bridgeVar = StringVar()
        tk.Label(self, text='Marcha/Paro').grid(row=0,column=0)
        self.bridge_button_on = ttk.Radiobutton(self, text='ON', value='ON', variable=self.bridgeVar, command=self.setBridge)
        self.bridge_button_on.grid(row=0,column=1)
        self.bridge_button_off = ttk.Radiobutton(self, text='OFF', value='OFF', variable=self.bridgeVar, command=self.setBridge)
        self.bridge_button_off.grid(row=0,column=2)
        self.bridge_button_auto = ttk.Radiobutton(self, text='AUTO', value='AUTO', variable=self.bridgeVar, command=self.setBridge)
        self.bridge_button_auto.grid(row=0,column=3)
        self.bridge_button_auto.invoke()


        tk.Label(self, text='Temp. Interior').grid(row=1,column=0)
        self.t_int_label = MyLabel(self, func=self.getTempInt, relief=SUNKEN, width=5)
        self.t_int_label.grid(row=1,column=1)

        self.tempInt = StringVar()
        self.temp_int_entry = ttk.Entry(self, textvariable=self.tempInt, width=5)
        self.temp_int_entry.grid(row=1,column=2)
        #temp_int_entry.config(state='disabled')
        self.t_int_checkbutton = tk.Checkbutton(self, text='Simulado', command=self.setTempIntSimulated)
        self.t_int_checkbutton.grid(row=1,column=3)


        tk.Label(self, text='Temp. Exterior').grid(row=2,column=0)
        MyLabel(self, func=self.getTempExt, relief=SUNKEN, width=5).grid(row=2,column=1)

        self.tempExt = StringVar()
        temp_ext_entry = ttk.Entry(self, textvariable=self.tempExt, width=5)
        temp_ext_entry.grid(row=2,column=2)
        self.t_ext_checkbutton = tk.Checkbutton(self, text='Simulado', command=self.setTempExtSimulated)
        self.t_ext_checkbutton.grid(row=2,column=3)

        self.update()



    def setBridge(self):

        outboundMessage = self.getOutboundMessage()
        outboundMessage['forced']['bridge'] = self.bridgeVar.get()



    def setTempIntSimulated(self):

        if self.tempIntSimulated:
            self.tempIntSimulated = False
            self.temp_int_entry.config(state='normal')
            #self.temp_int_entry.delete(0, END)
            outboundMessage = self.getOutboundMessage()
            outboundMessage['forced']['int'] = ''

        else:
            self.tempIntSimulated = True
            outboundMessage = self.getOutboundMessage()
            outboundMessage['forced']['int'] = self.tempInt.get()
            self.temp_int_entry.config(state='disabled')




    def setTempExtSimulated(self):

        if self.tempExtSimulated:
            self.tempExtSimulated = False

        else:
            self.tempExtSimulated = True
            outboundMessage = self.getOutboundMessage()
            outboundMessage['forced']['ext'] = self.tempExt.get()


    def update(self):

        self.readTempInt()
        self.readTempExt()

        self.after(500, self.update)



    def readTempInt(self):

        inboundMessage = self.getInboundMessage()

        try:
            self.tempInt = inboundMessage['sensor'][0]['value']

        except:
            pass



    def readTempExt(self):

        inboundMessage = self.getInboundMessage()

        try:
            self.tempExt = inboundMessage['sensor'][1]['value']

        except:
            pass



    def getTempInt(self):
        return self.tempInt



    def getTempExt(self):
        return self.tempExt



class Application(ttk.Frame): #, socket.socket):

    def __init__(self, main_window, targetServer, bufferSizeMsg):

        ttk.Frame.__init__(self, main_window)                               # Iniciando clase como Frame
        #socket.socket.__init__(self, socket.AF_INET, socket.SOCK_STREAM)    # Iniciando clase como socket
        self.socket = None

        # Parametros de comunicacion con el servidor
        self.targetServer = targetServer
        self.bufferSizeMsg = bufferSizeMsg

        main_window.title("GRUPO A3I - Panel de control")

        # Mensaje entrante desde el backend
        self.inboundMessage  = {}

        # Mensage saliente. Settea consignas y valores forzados. Se envía al backend
        self.outboundMessage = {'origin':'front-end', 'action':'none', 'forced':{'bridge':'AUTO','int':'','ext':''}}

        # Threading la comunicación como cliente con el backend (servidor)
        '''
        self.thread = threading.Thread(target=self.communicate)
        self.thread.daemon = True
        self.thread.start()
        '''
        self.createCommThread()
        while self.inboundMessage == {}:
            pass
        #time.sleep(1)
        ############

        #self.thread = threading.Thread(target=self.communicate)
        #self.thread.daemon = False #True
        #self.thread.start()

        #self.thread.join()
        #############

        # Crear el panel de pestañas.
        self.notebook = ttk.Notebook(self)

        # Crear el contenido de cada una de las pestañas.
        self.frame_info = MyInfoFrame(self, func=self.getInboundMessage)
        self.frame_settings = MySettingsFrame(self, func_2=self.getOutboundMessage)
        self.frame_advanced = MyAdvancedFrame(self, func_1=self.getInboundMessage, func_2=self.getOutboundMessage)

        # Añadirlas al panel con su respectivo texto.
        self.notebook.add(self.frame_info, text="Info", padding=20)
        self.notebook.add(self.frame_settings,  text="Settings", padding=20)
        self.notebook.add(self.frame_advanced,  text="Avanzado", padding=20)

        # Margenes
        self.notebook.pack(padx=10, pady=10)
        self.pack()


    def __del__(self):
        if self.socket != None:
            self.socket.close()


    def getInboundMessage(self):
        return self.inboundMessage


    def getOutboundMessage(self):
        return self.outboundMessage


    def setOutboundMessage(self, outboundMessage):
        self.outboundMessage = outboundMessage


    def createCommThread(self):
        self.thread = threading.Thread(target=self.communicate)
        self.thread.daemon = False #True
        self.thread.start()
        #self.thread.join()



    def communicate(self):

        try:

            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

            logging.debug('Connecting to %s', self.targetServer)
            self.socket.connect(self.targetServer)


            logging.debug('Sending message %s', self.outboundMessage)        self.socket.sendall(json.dumps(self.outboundMessage).encode('utf-8'))


            inboundMessage = self.socket.recv(self.bufferSizeMsg)

            logging.debug('Inbound Message %s', inboundMessage)

            self.inboundMessage = json.loads(inboundMessage)

            #self.close()


        except socket.timeout as errorTimeoutMsg:
            time.sleep(2)
            print(errorTimeoutMsg)
            #input("Socket Timeout...")


        except socket.gaierror as errorGaiMsg:
            time.sleep(2)
            print(errorGaiMsg)  
            #input("Socket Gaierror")


        except socket.error as errorMsg:
            time.sleep(2)
            print(errorMsg)
            #input("Socket Error...")

        finally:
            self.socket.close()
            #self.after(2000, self.communicate)
            self.after(500, self.createCommThread)


        #self.after(2000, self.communicate)




main_window = tk.Tk()
targetServer =  ('raspberrypi.local', 5005)
bufferSizeMsg = 2048

app = Application(main_window, targetServer, bufferSizeMsg)
app.mainloop()

Ошибка

Если сервер TCP не работает, я получаюследующее сообщение об ошибке:

RuntimeError: main thread is not in main loop
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...