Comment changer la couleur d'arrière-plan de la boîte dans Tkinter avec une action


Lucas Abreu
import platform
import subprocess
from tkinter import *

    ###IPs to use
iptoscan = {
  'test': '8.8.8.8',
  'test 2' : '7.7.7.7',
  'test 3' : '1.1.1.1'
}


    ###Ping function
def ping(ipAddr: object, timeout: object = 100) -> object:
      if platform.system().lower() == 'windows':
          numFlag = '-n'
      else:
          numFlag = '-c'
      global completedPing
      completedPing = subprocess.run(['ping', numFlag, '1', '-w', str(timeout), ipAddr],
                                  stdout=subprocess.PIPE,  # Capture standard out
                                  stderr=subprocess.STDOUT)  # Capture standard error
      if completedPing.returncode == 0: # I need this if command to send the IP address and a True command
          pingstatus = "Network Active "  # Active ping response
      else:                             # I need this to send the IP plus a False command
          pingstatus = "Network Error "  # No ping response
      print(pingstatus + ipAddr)
      return (completedPing.returncode == 0) and (b'TTL=' in completedPing.stdout)

    ###Function to ping from dictionary
def multiping():
  for ips in iptoscan:
      ping(iptoscan[ips])
  if completedPing.returncode == 0:
      return True
  else:
      print("notworking")

Ma question
Au lieu d'utiliser un "ButtonPress", je veux que les cases changent avec le résultat du ping, true devient vert faux reste rouge. Donc, fondamentalement, dès que le code s'exécute, je veux qu'il envoie un ping du dictionnaire et si les résultats sont vrais, je veux qu'il change la couleur de chaque boîte.

class OneSquare():
  def __init__(self, can, start_x, start_y, size):
      self.can=can
      self.id = self.can.create_rectangle((start_x, start_y,
                start_x+size, start_y+size), fill="red")
      self.can.tag_bind(self.id, "<ButtonPress-1>", self.set_color)

      self.color_change=True

  def set_color(self, event=None):
      self.color_change = not self.color_change
      color="red"
      if not self.color_change:
          color="green"
      self.can.itemconfigure(self.id, fill=color)

root = Tk()
canvas = Canvas(root)
canvas.grid(column=1, row=1, sticky=(N, S, E, W))

    #Boxes to display the network status
IP1=OneSquare(canvas, 1, 1, 30)
IP2=OneSquare(canvas, 1, 50, 30)
IP3=OneSquare(canvas, 1, 100, 30)

    #Exit button
Button(root, text="Exit", bg="orange",
     command=root.quit).grid(row=2)

multiping()

root.mainloop()
Stevo Mitric

Ce sont des questions un peu délicates car les requêtes ping appelées à partir du système d'exploitation peuvent avoir des retards lorsque le périphérique de destination est inaccessible. Cela entraînera des gels constants de tkinter et des retards dans votre programme au fur et à mesure de la boucle. Pour éviter de tels scénarios, le moyen le plus simple est d'utiliser des threads (dont tkinter n'aime pas).

Vous auriez besoin d'un thread séparé qui effectue ces requêtes en permanence et se termine lorsque tkinter le fait. Assurez-vous que vous n'effectuez aucun appel de ce thread vers vos widgets, car cela entraînera des erreurs inattendues et des plantages.

Voici un exemple simple que vous pouvez appliquer à votre code:

import subprocess, time
from tkinter import *
from threading import Thread


iptoscan = {                # Your IP list
  'test': '8.8.8.8',
  'test 2' : '7.7.7.7',
  'test 3' : '1.1.1.1'
}

def startPing():
    while root:
        for id in iptoscan:
            process = subprocess.Popen(['ping', iptoscan[id], '-n', '1'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # This is just for windows OS (the system i'm testing on), -n 1 if for sending only one ping request
            #labels[id][1] = process.stdout.read().split('\r\n')[2]  # for python 2.x
            labels[id][1] = str(process.stdout.read()).split('\\r\\n')[2]
        time.sleep(1)                   # Delay between ping requests

def updateLabels():
    for id in labels:
        if 'time=' in labels[id][1]:    # This could be our trigger for knowing if ping was successful
            labels[id][0].configure(bg = 'green', text = 'IP: ' +iptoscan[id] + ', time: ' + labels[id][1].split('time=')[1].split(' ')[0] )    # I'm updating the names also as you can see
        else:
            labels[id][0].configure(bg = 'dark orange', text = 'IP: ' +iptoscan[id] + ' ' +labels[id][1] )                                      # If the requst fails, display the message
    root.after(100, updateLabels)       # Continue the loop

root = Tk()
root.geometry('300x120')

labels = {}                             # We'll store the label widget and ping response in this dictionary
for id in iptoscan:
    label = Label(root, text = 'IP: ' + iptoscan[id] )
    label.pack(side='bottom',pady=10)
    labels[id] = [label, '']            # First element will be the widget and second one the response

Thread(target = startPing).start()      # Starting the thread to perform ping requests

root.after(100, updateLabels)           # Starting to loop the function that will update ping responses to our widgets
root.mainloop()
root = None

Articles connexes