621 lines
24 KiB
Python
621 lines
24 KiB
Python
import tkinter as tk
|
||
from tkinter import filedialog, messagebox, ttk
|
||
import threading
|
||
import socket
|
||
import time
|
||
import binascii
|
||
import requests
|
||
from concurrent.futures import ThreadPoolExecutor
|
||
|
||
|
||
def hexspace(data, n):
|
||
return " ".join([data[i : i + n] for i in range(0, len(data), n)])
|
||
|
||
|
||
class DataReceiver:
|
||
def __init__(self):
|
||
self.running = False
|
||
self.file_mode = False
|
||
self.file_path = None
|
||
self.manual_mode = False
|
||
|
||
def start_tcp(self, host, port, callback):
|
||
self.running = True
|
||
|
||
def run():
|
||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||
try:
|
||
# Подключаемся к серверу
|
||
s.connect((host, port))
|
||
|
||
# Отправляем строку "hello" в виде байтов
|
||
s.sendall("hello".encode("utf-8"))
|
||
|
||
# Основной цикл для получения данных
|
||
while self.running:
|
||
try:
|
||
# Получаем данные от сервера (максимум 1024 байта)
|
||
data = s.recv(1024)
|
||
|
||
if data:
|
||
try:
|
||
# Преобразуем байты в строку UTF-8
|
||
decoded_data = data
|
||
# Вызываем callback с декодированными данными
|
||
callback(decoded_data)
|
||
except UnicodeDecodeError:
|
||
print(f"Ошибка декодирования данных: {data}")
|
||
callback(data.decode("latin1", errors="replace"))
|
||
else:
|
||
# Если data пустое, соединение закрылось
|
||
print("Сервер закрыл соединение.")
|
||
break
|
||
except (socket.error, OSError) as e:
|
||
print(f"Ошибка приёма данных: {e}")
|
||
break
|
||
except Exception as e:
|
||
print(f"Ошибка подключения: {e}")
|
||
finally:
|
||
print("Соединение закрыто.")
|
||
self.running = False
|
||
|
||
threading.Thread(target=run, daemon=True).start()
|
||
|
||
def start_file(self, callback, timeout):
|
||
self.running = True
|
||
|
||
def run():
|
||
if not self.file_path:
|
||
return
|
||
with open(self.file_path, "r") as f:
|
||
while self.running:
|
||
if self.manual_mode:
|
||
continue
|
||
line = f.readline()
|
||
if line:
|
||
callback(eval(line.strip().split(" DEBUG ")[1]))
|
||
time.sleep(timeout)
|
||
|
||
threading.Thread(target=run, daemon=True).start()
|
||
|
||
def read_next_line(self, callback):
|
||
if not self.file_path:
|
||
return
|
||
with open(self.file_path, "r") as f:
|
||
line = f.readline()
|
||
if line:
|
||
callback(eval(line.strip().split(" DEBUG ")[1]))
|
||
|
||
def stop(self):
|
||
self.running = False
|
||
|
||
|
||
class DataSender:
|
||
def __init__(self):
|
||
self.targets = []
|
||
self.session = requests.Session()
|
||
self.session.headers.update({"Connection": "keep-alive"})
|
||
|
||
def add_target(self, host, port):
|
||
self.targets.append((host, port))
|
||
|
||
def send(self, data):
|
||
"""Многопоточная отправка данных на все указанные цели."""
|
||
if not self.targets:
|
||
print("No targets available to send data.")
|
||
return
|
||
|
||
def send_to_target(target):
|
||
host, port = target
|
||
url = f"http://{host}:{port}/API/"
|
||
# url = f"http://{host}:{port}/"
|
||
try:
|
||
for k, v in data.items():
|
||
par = "SetText" if k.split(".")[1] == "Text" else "SetImage"
|
||
params = {
|
||
"Function": par,
|
||
"Input": 51,
|
||
"SelectedName": k,
|
||
"Value": v,
|
||
}
|
||
|
||
# params = {
|
||
# "action": "set_text",
|
||
# "layer": f"SCORE STRIPE FLAGS INTRO\Data\{k}",
|
||
# "value": str(v),
|
||
# "channel": "live1",
|
||
# }
|
||
|
||
# params = {
|
||
# "action": "set_data_source_query_parameter",
|
||
# "data_source": "timer_data",
|
||
# "parameter": k,
|
||
# "value": v,
|
||
# }
|
||
# params = { "action" : "set_data_source_query_parameter", "data_source" : "XML/JSON Source 11", "parameter" : "TIMER", "value" : "London" }
|
||
# print(url, params)
|
||
response = self.session.get(url, params=params, timeout=5)
|
||
# response = self.session.post(url, params=params)
|
||
# print(response.headers)
|
||
response.raise_for_status() # Проверяет, есть ли ошибки HTTP
|
||
except requests.exceptions.RequestException as e:
|
||
print(f"Error sending data to {host}:{port}: {e}")
|
||
|
||
# Используем ThreadPoolExecutor для многопоточной отправки
|
||
with ThreadPoolExecutor(
|
||
max_workers=5
|
||
) as executor: # max_workers можно настроить
|
||
executor.map(send_to_target, self.targets)
|
||
|
||
def check_vmix_connections(self):
|
||
"""Проверяем соединения для всех целей и обновляем индикаторы."""
|
||
for index, (host_entry, port_entry) in enumerate(self.target_entries):
|
||
host = host_entry.get()
|
||
port = port_entry.get()
|
||
|
||
if not port.isdigit():
|
||
continue # Пропускаем некорректные порты
|
||
|
||
connected = self.sender.check_vmix_connection(host, int(port))
|
||
self.update_connection_indicator(index, connected)
|
||
|
||
def update_connection_indicator(self, index, connected):
|
||
"""Обновляем цвет индикатора для определенной цели."""
|
||
color = "green" if connected else "red"
|
||
self.connection_indicators[index].itemconfig("indicator", fill=color)
|
||
|
||
def check_vmix_connection(self, host, port):
|
||
url = f"http://{host}:{port}/API/"
|
||
# url = f"http://{host}:{port}"
|
||
try:
|
||
response = self.session.get(url, timeout=2) # Таймаут в секундах
|
||
return response.status_code == 200
|
||
except requests.exceptions.RequestException:
|
||
return False
|
||
|
||
|
||
class DataProcessor:
|
||
def process_protocol_a(self, data):
|
||
return data.upper()
|
||
|
||
def process_protocol_b(self, data):
|
||
return data[::-1]
|
||
|
||
def process_chine(self, data):
|
||
cdata = binascii.hexlify(data)
|
||
ddata = cdata.decode("utf-8", errors="replace").upper()
|
||
edata = hexspace(ddata, 2)
|
||
temp_data = edata.split()
|
||
temp_new = [int(t, 16) for t in temp_data]
|
||
# print(temp_new)
|
||
timer = f"{temp_new[3]}:{temp_new[2]}"
|
||
seconds = temp_new[5]
|
||
milliseconds = str(temp_new[4])[0]
|
||
if int(seconds) > 4:
|
||
temp = float(f"{seconds}.{milliseconds}")
|
||
if int(milliseconds) >= 5:
|
||
seconds = int(seconds) + 1
|
||
else:
|
||
seconds = seconds
|
||
timer_attack = seconds
|
||
timer_color = "#59358A"
|
||
else:
|
||
timer_attack = f"{seconds}.{milliseconds}"
|
||
timer_color = "#FF6A13"
|
||
if temp_new[0] in [16, 24]:
|
||
timer_attack = ""
|
||
timer_color = "#000000"
|
||
data = {
|
||
"12sec.Text": timer_attack,
|
||
"12SecBackground.Fill.Color": timer_color,
|
||
}
|
||
return data
|
||
|
||
def process_protocol_saratov(self, data):
|
||
if data == "\xff":
|
||
return
|
||
|
||
cdata = binascii.hexlify(data)
|
||
ddata = cdata.decode("utf-8").upper()
|
||
edata = hexspace(ddata, 2)
|
||
temp = edata.split("FF 52")
|
||
for i in temp:
|
||
try:
|
||
minutes = int(i.split()[5], 16)
|
||
seconds = int(i.split()[6], 16)
|
||
timer_str = (
|
||
f"{minutes}:{seconds:02d}"
|
||
if seconds < 60
|
||
else f"{minutes}.{seconds-128}"
|
||
)
|
||
|
||
seconds_24 = int(i.split()[7], 16)
|
||
timer_attack = ""
|
||
timer_pic = "D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\\24Sec_Orange.png"
|
||
if seconds_24 != 255:
|
||
if seconds_24 > 127:
|
||
seconds_24 -= 128
|
||
timer_attack = f"{seconds_24//10}.{seconds_24%10}"
|
||
timer_pic = "D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\\24Sec_Red.png"
|
||
else:
|
||
timer_attack = seconds_24
|
||
|
||
quarter = int(i.split()[14][1], 16)
|
||
timeout1 = int(i.split()[10], 16)
|
||
timeout2 = int(i.split()[11], 16)
|
||
score1 = int(i.split()[8], 16)
|
||
score2 = int(i.split()[9], 16)
|
||
foul1 = int(i.split()[12], 16)
|
||
foul2 = int(i.split()[13], 16)
|
||
|
||
data = {
|
||
"TIMER.Text": timer_str,
|
||
"ATTACK.Text": timer_attack,
|
||
"Score_Home.Text": score1,
|
||
"Score_Away.Text": score2,
|
||
"fouls1.Source": f"D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\Home_{foul1}.png",
|
||
"fouls2.Source": f"D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\Away_{foul2}.png",
|
||
"24SECBACKGROUND.Source": timer_pic,
|
||
}
|
||
# data = {
|
||
# "Timer": timer_str,
|
||
# # "Attack": timer_attack,
|
||
# # "Score1": score1,
|
||
# # "Score2": score2,
|
||
# # "fouls1.Source": f"D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\Home_{foul1}.png",
|
||
# # "fouls2.Source": f"D:\Графика\БАСКЕТБОЛ\ЕДИНАЯ ЛИГА ВТБ 2022-2023\Scorebug Indicators\Away_{foul2}.png",
|
||
# # "24SECBACKGROUND.Source": timer_pic,
|
||
# }
|
||
|
||
except (IndexError, ValueError):
|
||
continue
|
||
|
||
return data
|
||
|
||
|
||
class App:
|
||
def __init__(self, root):
|
||
self.receiver = DataReceiver()
|
||
self.sender = DataSender()
|
||
self.processor = DataProcessor()
|
||
|
||
self.protocol_var = tk.StringVar(value="Protocol A")
|
||
self.mode_var = tk.StringVar(value="Log File")
|
||
self.read_mode_var = tk.StringVar(value="Automatic")
|
||
self.timeout_var = tk.DoubleVar(value=0.5)
|
||
|
||
self.create_ui(root)
|
||
self.start_vmix_connection_check()
|
||
|
||
def display_data(self, data):
|
||
protocol = self.protocol_var.get()
|
||
if protocol == "Protocol A":
|
||
processed_data = self.processor.process_protocol_a(data)
|
||
elif protocol == "Protocol B":
|
||
processed_data = self.processor.process_protocol_b(data)
|
||
elif protocol == "Saratov":
|
||
processed_data = self.processor.process_protocol_saratov(data)
|
||
elif protocol == "Chine":
|
||
processed_data = self.processor.process_chine(data)
|
||
else:
|
||
processed_data = data
|
||
|
||
if processed_data:
|
||
self.update_send_status(True)
|
||
self.sender.send(processed_data)
|
||
self.update_send_status(False)
|
||
|
||
self.log_text.insert(tk.END, f"{data}\n")
|
||
self.log_text.see(tk.END)
|
||
|
||
self.processed_data_text.insert(tk.END, f"{processed_data}\n")
|
||
self.processed_data_text.see(tk.END)
|
||
|
||
def create_ui(self, root):
|
||
root.title("Data Processing Application")
|
||
|
||
self.notebook = ttk.Notebook(root)
|
||
self.notebook.pack(fill="both", expand=True)
|
||
|
||
self.settings_frame = tk.Frame(self.notebook)
|
||
self.game_frame = tk.Frame(self.notebook)
|
||
|
||
self.notebook.add(self.settings_frame, text="Settings")
|
||
self.notebook.add(self.game_frame, text="Game")
|
||
|
||
self.create_settings_ui(self.settings_frame)
|
||
self.create_game_ui(self.game_frame)
|
||
|
||
def create_settings_ui(self, frame):
|
||
frame_receive = tk.LabelFrame(frame, text="Receive Data")
|
||
frame_receive.pack(fill="x", padx=5, pady=5)
|
||
|
||
tk.Label(frame_receive, text="Select Mode:").pack(side="left", padx=5, pady=5)
|
||
tk.OptionMenu(
|
||
frame_receive,
|
||
self.mode_var,
|
||
"Log File",
|
||
"TCP Connection",
|
||
command=self.update_mode,
|
||
).pack(side="left", padx=5, pady=5)
|
||
|
||
self.file_button = tk.Button(
|
||
frame_receive, text="Select Log File", command=self.select_file
|
||
)
|
||
|
||
self.tcp_frame = tk.Frame(frame_receive)
|
||
tk.Label(self.tcp_frame, text="IP:").pack(side="left")
|
||
self.tcp_ip_entry = tk.Entry(self.tcp_frame)
|
||
self.tcp_ip_entry.pack(side="left", padx=5)
|
||
self.tcp_ip_entry.insert(0, "192.168.127.254") # например, default IP адрес
|
||
tk.Label(self.tcp_frame, text="Port:").pack(side="left")
|
||
self.tcp_port_entry = tk.Entry(self.tcp_frame)
|
||
self.tcp_port_entry.pack(side="left", padx=5)
|
||
self.tcp_port_entry.insert(0, 1993) # например, default порт
|
||
|
||
self.read_mode_frame = tk.Frame(frame_receive)
|
||
tk.Label(self.read_mode_frame, text="Read Mode:").pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
tk.OptionMenu(
|
||
self.read_mode_frame,
|
||
self.read_mode_var,
|
||
"Automatic",
|
||
"Manual",
|
||
command=self.update_read_mode,
|
||
).pack(side="left", padx=5, pady=5)
|
||
tk.Label(self.read_mode_frame, text="Timeout (s):").pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
self.timeout_entry = tk.Entry(
|
||
self.read_mode_frame, textvariable=self.timeout_var
|
||
)
|
||
self.timeout_entry.pack(side="left", padx=5)
|
||
self.manual_read_button = tk.Button(
|
||
self.read_mode_frame, text="Read Next Line", command=self.read_next_line
|
||
)
|
||
|
||
self.start_button = tk.Button(
|
||
frame_receive, text="Start", command=self.start_receiving
|
||
)
|
||
self.start_button.pack(side="left", padx=5, pady=5)
|
||
tk.Button(frame_receive, text="Stop", command=self.stop_receiving).pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
|
||
frame_process = tk.LabelFrame(frame, text="Process Data")
|
||
frame_process.pack(fill="x", padx=5, pady=5)
|
||
|
||
tk.Label(frame_process, text="Select Protocol:").pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
tk.OptionMenu(
|
||
frame_process,
|
||
self.protocol_var,
|
||
"Protocol A",
|
||
"Protocol B",
|
||
"Saratov",
|
||
"Chine",
|
||
).pack(side="left", padx=5, pady=5)
|
||
|
||
frame_send = tk.LabelFrame(frame, text="Send Data")
|
||
frame_send.pack(fill="x", padx=5, pady=5)
|
||
|
||
self.target_count_var = tk.IntVar(value=1)
|
||
tk.Label(frame_send, text="Number of Targets:").pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
tk.Spinbox(
|
||
frame_send,
|
||
from_=1,
|
||
to=10,
|
||
textvariable=self.target_count_var,
|
||
command=self.update_targets,
|
||
).pack(side="left", padx=5, pady=5)
|
||
|
||
self.targets_frame = tk.Frame(frame_send)
|
||
self.targets_frame.pack(fill="x", padx=5, pady=5)
|
||
self.target_entries = []
|
||
self.update_targets()
|
||
|
||
tk.Button(frame_send, text="Send Data", command=self.send_data).pack(
|
||
side="left", padx=5, pady=5
|
||
)
|
||
|
||
self.update_mode(self.mode_var.get())
|
||
|
||
def create_game_ui(self, frame):
|
||
frame_status = tk.LabelFrame(frame, text="Status")
|
||
frame_status.pack(fill="x", padx=5, pady=5)
|
||
|
||
# Canvas indicators
|
||
self.receive_status_canvas = tk.Canvas(frame_status, width=20, height=20)
|
||
self.receive_status_canvas.create_oval(
|
||
2, 2, 18, 18, fill="red", tags="indicator"
|
||
)
|
||
self.receive_status_canvas.pack(side="left", padx=10, pady=5)
|
||
tk.Label(frame_status, text="Receiving Data").pack(side="left")
|
||
|
||
self.send_status_canvas = tk.Canvas(frame_status, width=20, height=20)
|
||
self.send_status_canvas.create_oval(2, 2, 18, 18, fill="red", tags="indicator")
|
||
self.send_status_canvas.pack(side="left", padx=10, pady=5)
|
||
tk.Label(frame_status, text="Sending Data").pack(side="left")
|
||
|
||
frame_log = tk.LabelFrame(frame, text="Log")
|
||
frame_log.pack(fill="both", expand=True, padx=5, pady=5)
|
||
|
||
self.log_text = tk.Text(frame_log, height=10, state="normal")
|
||
self.log_text.pack(fill="both", expand=True, padx=5, pady=5)
|
||
|
||
frame_processed = tk.LabelFrame(frame, text="Processed Data")
|
||
frame_processed.pack(fill="both", expand=True, padx=5, pady=5)
|
||
|
||
self.processed_data_text = tk.Text(frame_processed, height=10, state="normal")
|
||
self.processed_data_text.pack(fill="both", expand=True, padx=5, pady=5)
|
||
|
||
# self.vmix_status_indicator = tk.Canvas(frame_status, width=20, height=20)
|
||
# self.vmix_status_indicator.pack(side="left", padx=5, pady=5)
|
||
# self.update_vmix_indicator(False)
|
||
|
||
# tk.Label(frame_status, text="vMix Connection").pack(side="left", padx=5, pady=5)
|
||
|
||
# def update_vmix_indicator(self, connected):
|
||
# self.vmix_status_indicator.delete("all")
|
||
# color = "green" if connected else "red"
|
||
# self.vmix_status_indicator.create_oval(2, 2, 18, 18, fill=color)
|
||
|
||
# def check_vmix_connection(self):
|
||
# if not self.target_entries:
|
||
# return False
|
||
|
||
# host_entry, port_entry = self.target_entries[0] # Проверяем первую цель
|
||
# host = host_entry.get()
|
||
# port = port_entry.get()
|
||
# if not port.isdigit():
|
||
# return False
|
||
|
||
# connected = self.sender.check_vmix_connection(host, int(port))
|
||
# self.update_vmix_indicator(connected)
|
||
# return connected
|
||
|
||
def check_vmix_connections(self):
|
||
for index, (host_entry, port_entry) in enumerate(self.target_entries):
|
||
host = host_entry.get()
|
||
port = port_entry.get()
|
||
|
||
if not port.isdigit():
|
||
continue # Пропускаем некорректные порты
|
||
|
||
connected = self.sender.check_vmix_connection(host, int(port))
|
||
self.update_connection_indicator(index, connected)
|
||
|
||
def update_connection_indicator(self, index, connected):
|
||
color = "green" if connected else "red"
|
||
self.connection_indicators[index].itemconfig("indicator", fill=color)
|
||
|
||
def start_vmix_connection_check(self):
|
||
"""Периодически проверяем соединения."""
|
||
|
||
def check_loop():
|
||
while True:
|
||
self.check_vmix_connections()
|
||
time.sleep(1)
|
||
|
||
threading.Thread(target=check_loop, daemon=True).start()
|
||
|
||
def update_mode(self, mode):
|
||
if mode == "Log File":
|
||
self.file_button.pack(side="left", padx=5, pady=5)
|
||
self.tcp_frame.pack_forget()
|
||
self.read_mode_frame.pack(fill="x", padx=5, pady=5)
|
||
self.start_button.config(state="normal")
|
||
else:
|
||
self.file_button.pack_forget()
|
||
self.read_mode_frame.pack_forget()
|
||
self.tcp_frame.pack(side="left", padx=5, pady=5)
|
||
self.start_button.config(state="normal")
|
||
|
||
def update_read_mode(self, mode):
|
||
if mode == "Automatic":
|
||
self.timeout_entry.config(state="normal")
|
||
self.manual_read_button.pack_forget()
|
||
self.receiver.manual_mode = False
|
||
else:
|
||
self.timeout_entry.config(state="disabled")
|
||
self.manual_read_button.pack(side="left", padx=5, pady=5)
|
||
self.receiver.manual_mode = True
|
||
|
||
def select_file(self):
|
||
self.receiver.file_path = filedialog.askopenfilename()
|
||
|
||
def start_receiving(self):
|
||
self.start_button.config(state="disabled")
|
||
mode = self.mode_var.get()
|
||
if mode == "Log File":
|
||
if self.receiver.file_path:
|
||
timeout = (
|
||
self.timeout_var.get()
|
||
if self.read_mode_var.get() == "Automatic"
|
||
else 0
|
||
)
|
||
self.receiver.start_file(self.display_data, timeout)
|
||
self.update_receive_status(True)
|
||
else:
|
||
messagebox.showerror("Error", "No file selected.")
|
||
elif mode == "TCP Connection":
|
||
host = self.tcp_ip_entry.get()
|
||
port = self.tcp_port_entry.get()
|
||
if host and port.isdigit():
|
||
self.receiver.start_tcp(host, int(port), self.display_data)
|
||
self.update_receive_status(True)
|
||
else:
|
||
messagebox.showerror("Error", "Invalid IP or port.")
|
||
self.start_button.config(state="normal")
|
||
|
||
def stop_receiving(self):
|
||
self.receiver.stop()
|
||
self.start_button.config(state="normal")
|
||
self.update_receive_status(False)
|
||
|
||
def read_next_line(self):
|
||
self.receiver.read_next_line(self.display_data)
|
||
|
||
def update_receive_status(self, active):
|
||
color = "green" if active else "red"
|
||
self.receive_status_canvas.itemconfig("indicator", fill=color)
|
||
|
||
def update_send_status(self, active):
|
||
color = "green" if active else "red"
|
||
self.send_status_canvas.itemconfig("indicator", fill=color)
|
||
|
||
def update_targets(self):
|
||
for widget in self.targets_frame.winfo_children():
|
||
widget.destroy()
|
||
|
||
self.target_entries = []
|
||
self.connection_indicators = [] # Храним индикаторы состояния
|
||
|
||
for i in range(self.target_count_var.get()):
|
||
target_frame = tk.Frame(self.targets_frame)
|
||
target_frame.pack(fill="x", pady=2)
|
||
|
||
tk.Label(target_frame, text=f"Target {i+1} Host:").pack(side="left")
|
||
host_entry = tk.Entry(target_frame)
|
||
host_entry.pack(side="left", padx=5)
|
||
host_entry.insert(0, "127.0.0.1")
|
||
|
||
tk.Label(target_frame, text="Port:").pack(side="left")
|
||
port_entry = tk.Entry(target_frame)
|
||
port_entry.pack(side="left", padx=5)
|
||
port_entry.insert(0, 8088)
|
||
|
||
indicator = tk.Canvas(target_frame, width=20, height=20)
|
||
indicator.create_oval(2, 2, 18, 18, fill="red", tags="indicator")
|
||
indicator.pack(side="left", padx=5)
|
||
|
||
self.target_entries.append((host_entry, port_entry))
|
||
self.connection_indicators.append(indicator)
|
||
|
||
def send_data(self):
|
||
data = "Sample data to send"
|
||
if not data:
|
||
messagebox.showerror("No Data", "No data to send.")
|
||
return
|
||
|
||
self.sender.targets = []
|
||
for host_entry, port_entry in self.target_entries:
|
||
host = host_entry.get()
|
||
port = port_entry.get()
|
||
if host and port.isdigit():
|
||
self.sender.add_target(host, int(port))
|
||
|
||
# self.update_send_status(True)
|
||
self.sender.send(data)
|
||
# self.update_send_status(False)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
root = tk.Tk()
|
||
app = App(root)
|
||
root.mainloop()
|