Как оптимизировать использование процессора с python потоками и подпроцессом? - PullRequest
0 голосов
/ 16 января 2020

Уважаемые продвинутые python программисты,

Я новичок Python, но мне пришлось разработать GUI для научных c измерений. Я решил использовать старый добрый tkinter, так как он мне показался простым по сравнению с QT. Основная программа, которую я публикую ниже, используется для создания кнопок. Кнопки Pu sh открывают внешние сценарии python, выполняющие измерения с помощью «subprocess.Popen». Измерения отправляются обратно через сокеты. Я также использовал некоторые потоки для обновления sh показаний измерений.

Моя программа работает довольно хорошо, но она слишком жадная для моего Raspberry 3B +. Когда я запускаю его, он использует 130% процессора. Мое окно tkinter зависнет (но измерения все еще работают), если я дважды нажму sh кнопки «профиля». Как я уже говорил, я новичок, и в моей программе может быть много fl aws для эксперта. Я был бы признателен за помощь в оптимизации моего кода без переписывания всего.

Если бы кто-то мог указать, что использует много ЦП, я был бы очень признателен. При кодировании я обнаружил некоторые проблемы:

-Я могу перезапустить слишком много потоков в каждой операции, и ни один из них никогда не завершается. Любая идея, чтобы помочь мне закончить потоки?

-Я вызываю много модулей, и некоторые из них жадные (например, модуль «запросов»)

-Tk - это oldi sh, и я должен изучить Qt но это займет время, и я сделаю это позже, когда этот проект будет запущен.

Вот мой код, я прошу прощения, если он очень длинный:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created 11/12/19

@author: Lionel 
"""
import tkinter as tk
from tkinter import *
import threading
import tk_tools
import subprocess
import socket
from PIL import ImageTk, Image
import requests
import re
from tkinter import messagebox
import time

global status
speed=0
current=0
depth=0
argdepth=0
newdepth=0
bgcolor=str("light gray")

"""-------------------------DECLARATIONS PORTS UDP GUI-------------------------"""
UDP_IP_ADDRESS = "192.168.203.28"
UDP_PORT_CURRENT = 9001
UDP_PORT_POS = 9002
UDP_PORT_DEPTH = 9003
UDP_PORT_MESG = 9004
UDP_PORT_SPEED = 9005
serverSockCurrent = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSockCurrent.bind((UDP_IP_ADDRESS, UDP_PORT_CURRENT))
serverSockPosition = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSockPosition.bind((UDP_IP_ADDRESS, UDP_PORT_POS))
serverSockDepth = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSockDepth.bind((UDP_IP_ADDRESS, UDP_PORT_DEPTH))
serverSockMessage = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSockMessage.bind((UDP_IP_ADDRESS, UDP_PORT_MESG))
serverSockSpeed = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
serverSockSpeed.bind((UDP_IP_ADDRESS, UDP_PORT_SPEED))


def readrelay():
    #récupérer état
    r = requests.get('http://192.168.203.129/stateFull.xml?&noReply=0')
    string = r.text
    subStr = re.findall(r'relaystate>(.+?)</relaystate',string)
    global status
    global relaystatus
    status=int(', '.join(subStr))
    if status ==1:
        relaystatus="POWER IS ON" 
        print("relay is on")
        measurements()
        read = subprocess.Popen(['/usr/bin/python3','/home/pi/read.py'], shell=False)
        time.sleep(2)
        read.terminate()
    elif status ==0:
        relaystatus="POWER IS OFF"
        print("relay is off")
    else:
        print("relay status undefined, no network") 
        relaystatus="UNDEFINED"

def toggle(): 
    if var1.get() == "POWER IS ON":
        status=1
        print("relay on...")
        print("status=", status)
        try:
            requests.get('http://192.168.203.129/state.xml?relayState=1&noReply=0')
        except requests.exceptions.ConnectionError as err:
            if "BadStatusLine" in repr(err):
                pass
    else:
        status=0
        try:
            requests.get('http://192.168.203.129/state.xml?relayState=0&noReply=0')
        except requests.exceptions.ConnectionError as err:
            if "BadStatusLine" in repr(err):
                pass
        print("relay off...")
        print("status=", status)

def scaler():
    scale = Scale(MyWindow, from_=0, to_=40, length=350,tickinterval=5, bg="SkyBlue2", troughcolor = "dodger blue", bd =0, state="active")
    scale.place(x=825, y=30)
    while True:       
        scale.set(newdepth)

def run_scaler():
    tscaler=threading.Thread(target=scaler)
    tscaler.start()

def measurements():
    run_position()
    run_depth()
    run_message()
    run_current()
    run_speed()
    run_scaler()

"""-------------------------Méthode lancement profil auto---------------------"""
def profil():
    argdepth=arg1.get()
    sub=arg2.get()
    speed=arg3.get()
    global pyprofil
    pyprofil=subprocess.Popen(['/usr/bin/python3','/home/pi/profilspeed.py',argdepth,sub,speed], shell=False)

def run_profil():
    readrelay()
    print("status on start=", status)
    if status ==1:
        profil()
        measurements()  
    elif status==0:
        root = Tk()
        root.withdraw()
        messagebox.showwarning("Warning","POWER IS OFF")
        root.destroy()
    else:
        root = Tk()
        root.withdraw()
        messagebox.showwarning("Warning","NO NETWORK")
        root.destroy()

def run_stop():
    global pystop
    pystop =subprocess.Popen(['/usr/bin/python3','/home/pi/stop.py'],shell=False)
    pyprofil.terminate()
    measurements()
    pystop.terminate()

def rescue():
    global pyrescue
    pyprofil.terminate()
    pyrescue=subprocess.Popen(['/usr/bin/python3', '/home/pi/rescueblackout.py'], shell=False)

def run_rescue():
    rescue()
    measurements()

def emergency():
    subprocess.run("python3 emergency.py", shell=True)

def run_emergency():
    emergency()
    measurements()

def run_quit():
    MyWindow.destroy()
    exit()

"""-------------------------Méthode lecture courant----------------------------"""
def refreshcurrent():
    while True:
        datacurrent, addr = serverSockCurrent.recvfrom(1024)
        global current
        current = datacurrent.decode('utf-8').strip()

def run_current():
    tcurrent = threading.Thread(target=refreshcurrent)
    tcurrent.start()    

"""-------------------------Méthode lecture position----------------------------"""
def refreshposition():
    while True:
        datapos, addr = serverSockPosition.recvfrom(1024)
        global pos
        pos = datapos.decode('utf-8').strip()
        textpos.configure(text = str(pos))  

def run_position():
    tpos = threading.Thread(target=refreshposition)
    tpos.start()    

"""-------------------------Méthode lecture depth----------------------------"""
def refreshdepth():
    while True:
        datadepth, addr = serverSockDepth.recvfrom(1024)
        global depth, newdepth
        depth = datadepth.decode('utf-8').strip()
        #print("depth=",speth)
        textdepth.configure(text = str(depth))        
        if float(depth) < 0:
            newdepth = 0
        else:
            newdepth=int(100*float(depth))

def run_depth():
    tdepth = threading.Thread(target=refreshdepth)
    tdepth.start()    

"""-------------------------Méthode lecture messages----------------------------"""
def refreshmessage():
    while True:
        datamesg, addr = serverSockMessage.recvfrom(1024)
        mesg = datamesg.decode('utf-8').strip()
        strmesg = str(mesg)
        if strmesg.find("END") != -1:
                print ("LE PROFIL EST FINI!")
                pyprofil.terminate() 
#                if tprofil.is_alive():
#                    print ("LE THREAD PROFIL EST TOUJOURS VIVANT!")
        #print("depth=",speth)
        textmesg.configure(text = strmesg)
def run_message():
    tmessage=threading.Thread(target=refreshmessage)
    tmessage.start()

"""-------------------------Méthode lecture vitesse----------------------------"""
def refreshspeed():
    while True:
        dataspeed, addr = serverSockSpeed.recvfrom(1024)
        speed = dataspeed.decode('utf-8').strip()
        speed = int(speed) / 5000
        if speed < 0 and speed > -0.0005 :
            speed=0
        speed = round(speed,2)
        #print("speed=",speed)
        textspeed.configure(text = str(speed))

def run_speed():
    tspeed=threading.Thread(target=refreshspeed)
    tspeed.start()

"""-----------------------------GUI--------------------------------------------"""
# Fenêtre principale
MyWindow = Tk()
MyWindow.title("CTD AUTO PROFILING CRANE")
MyWindow.geometry("900x400")
MyWindow.configure(background=bgcolor)


#Création d'un widget Entry depth (Champ de saisie)
zone_texte1=Label(text="Max depth:")
zone_texte1.place(x=170,y=62)
zone_texte1.config(font="courier 15 bold",fg="DodgerBlue3", bg=bgcolor)
zone_texte1u=Label(text="(m)")
zone_texte1u.place(x=260,y=82)
zone_texte1u.config(font="courier 10 bold",fg="DodgerBlue3", bg=bgcolor)
arg1= tk.StringVar()
arg1.set(0.3)
Champ1 = Entry(MyWindow, textvariable= arg1, font = "courier 15 bold", bg ="white", fg="DodgerBlue3", width="4", bd=3)
Champ1.place(x=300,y=60)

#Création d'un widget Entry subsurface depth (Champ de saisie)
zone_texte2=Label(text="Subsurface:")
zone_texte2.place(x=390,y=62)
zone_texte2.config(font="courier 15 bold",fg="DodgerBlue3", bg=bgcolor)
zone_texte2u=Label(text="(m)")
zone_texte2u.place(x=490,y=82)
zone_texte2u.config(font="courier 10 bold",fg="DodgerBlue3", bg=bgcolor)
arg2= tk.StringVar()
arg2.set(0.15)
Champ2 = Entry(MyWindow, textvariable= arg2, font = "courier 15 bold", bg ="white", fg="DodgerBlue3", width="4", bd=3)
Champ2.place(x=530,y=60)

#Création d'un widget Entry speed (Champ de saisie)
zone_texte3=Label(text="Speed:")
zone_texte3.place(x=640,y=62)
zone_texte3.config(font="courier 15 bold",fg="DodgerBlue3", bg=bgcolor)
zone_texte3u=Label(text="(m/s)")
zone_texte3u.place(x=668,y=82)
zone_texte3u.config(font="courier 10 bold",fg="DodgerBlue3", bg=bgcolor)
arg3= tk.StringVar()
arg3.set(0.1)
Champ3 = Entry(MyWindow, textvariable= arg3, font = "courier 15 bold", bg ="white", fg="DodgerBlue3", width="4", bd=3)
Champ3.place(x=720,y=60)

# Création d'un bouton profil
Buttonprofil = Button(MyWindow, text ="Profil", command = run_profil)
Buttonprofil.config(text='PROFILE', fg='black', bg='dodger blue', activebackground='DodgerBlue3', activeforeground='white', bd=4)
Buttonprofil.config(font=('courier', 15, 'bold'))
Buttonprofil.place(x=10,y=60)

# Création d'un bouton STOP
Buttonstop = Button(MyWindow, text ="STOP", command = run_stop)
Buttonstop.config(text='STOP', fg='white', bg='red', activebackground='red3', activeforeground='black', bd=4)
Buttonstop.config(font=('courier', 15, 'bold'))
Buttonstop.place(x=10,y=120)

# Création d'un bouton Rescue
Buttonrescue = Button(MyWindow, text ="RETRIEVE", command = run_rescue)
Buttonrescue.config(text='RETRIEVE', fg='black', bg='DarkOrange1', activebackground='DarkOrange3',activeforeground='white', bd=4)
Buttonrescue.config(font=('courier', 15, 'bold'))
Buttonrescue.place(x=10,y=180)

# Création d'un bouton EMERGENCY
Buttonemergency = Button(MyWindow, text ="EMERGENCY", command= run_emergency)
Buttonemergency.config(text='EMERGENCY', fg='red', bg='white', activebackground='red', activeforeground='white', bd=4)
Buttonemergency.config(font=('courier', 15, 'bold'))
Buttonemergency.place(x=10,y=240)


#Création d'un bouton Exit
ButtonExit = Button(MyWindow, text ="QUIT", font = "courier 15 bold", command = run_quit, bd=4)
ButtonExit.place (x=10, y=340)

#Création zone de texte courant
zone_texte4=Label(text="Current:", font="courier 15 bold", bg=bgcolor)
zone_texte4.place(x=170,y=230)
zone_texte4u=Label(text="(mA)")
zone_texte4u.place(x=230,y=250)
zone_texte4u.config(font="courier 10 bold",fg="black", bg=bgcolor)

#gauge courant
my_gauge = tk_tools.Gauge(MyWindow, height = 110, width = 200,
                             min_value=-200,
                             max_value=2600,
                             divisions=13,
                             yellow_low=10,
                             label='Current',
                             unit='mA',
                             bg=bgcolor)
my_gauge.place(x=300, y=180)
def update_gauge():
    my_gauge.set_value(current)
    MyWindow.after(200, update_gauge)
MyWindow.after(50, update_gauge)

#Création zone de texte position
zone_texte5=Label(text="Position:", font="courier 15 bold", bg=bgcolor, fg="black")
zone_texte5.place(x=350,y=125)
zone_texte5u=Label(text="(ticks)")
zone_texte5u.place(x=396,y=145)
zone_texte5u.config(font="courier 10 bold",fg="black", bg=bgcolor)
textpos= Label(MyWindow,font = "courier 15 bold", bg ="black", fg="green2", width="10", bd=4)
textpos.place(x=470,y=125)

#Création zone de texte depth
zone_texte6=Label(text="Depth:", font="courier 15 bold", bg=bgcolor, fg="black")
zone_texte6.place(x=170,y=125)
zone_texte6u=Label(text="(m)")
zone_texte6u.place(x=210,y=145)
zone_texte6u.config(font="courier 10 bold",fg="black", bg=bgcolor)
textdepth= Label(MyWindow,font = "courier 15 bold", bg ="black", fg="green2", width="6", bd =4)
textdepth.place(x=250,y=125)

#Création zone de texte messages
zone_texte7=Label(text="STATUS:", font="courier 15 bold", bg=bgcolor)
zone_texte7.place(x=170,y=315)
textmesg= Label(MyWindow,font = "courier 15 bold", bg ="black", fg="DeepPink2", width="52", anchor="w", bd=4)
textmesg.place(x=170,y=345)

#Création zone de texte speed
zone_texte8=Label(text="Speed:", font="courier 15 bold", bg=bgcolor)
zone_texte8.place(x=620,y=125)
zone_texte8u=Label(text="(m/s)")
zone_texte8u.place(x=648,y=145)
zone_texte8u.config(font="courier 9 bold",fg="black", bg=bgcolor)
#print(type(textcurrent))
textspeed= Label(MyWindow,font = "courier 15 bold", bg ="black", fg="green2", width="6", bd=4)
textspeed.place(x=700,y=125)

#LOGO DT INSU
img1 = ImageTk.PhotoImage(Image.open("logodtm.png"))
imglabel = Label(MyWindow, image=img1)
imglabel.place(x=700, y=190)
imglabel.config(bg=bgcolor)     

#LOGO LIENS
img2 = ImageTk.PhotoImage(Image.open("liens.png"))
imglabel = Label(MyWindow, image=img2)
imglabel.place(x=600, y=210)
imglabel.config(bg=bgcolor)   

scaleinit = Scale(MyWindow, from_=0, to_=40, length=350,tickinterval=5, bg="SkyBlue2", troughcolor = "dodger blue", bd =0, state="active")
scaleinit.place(x=825, y=30)

readrelay()
var1 = tk.StringVar()
toggle = Checkbutton(MyWindow, onvalue="POWER IS ON", offvalue="POWER IS OFF", width=13,
                        indicatoron=False, 
                        background="red",activebackground="red",selectcolor="chartreuse2",
                        variable=var1, textvariable=var1,
                        bd=4, command=toggle)
var1.set(relaystatus)

print("status=", status)
if status ==1:
    toggle.select()
toggle.place(x=380,y=10)
toggle.config(font="courier 15 bold")

MyWindow.mainloop()

1 Ответ

0 голосов
/ 20 января 2020

Никто не поможет новичку с жадными нитями?

...